Commit 7ad374bd authored by Kostas Papadimitriou's avatar Kostas Papadimitriou
Browse files

Merge branch 'latest-quota' into feature-astakos-tables

Conflicts:
	snf-astakos-app/astakos/im/templates/im/projects/project_list.html
	snf-astakos-app/astakos/im/views.py
parents 385b7248 8bd1b76d
......@@ -86,58 +86,6 @@ def api_method(http_method=None):
return decorator
def _get_user_by_username(user_id):
try:
user = AstakosUser.objects.get(username=user_id)
except AstakosUser.DoesNotExist:
raise ItemNotFound('Invalid userid')
else:
response = HttpResponse()
response.status = 200
user_info = {'id': user.id,
'username': user.username,
'email': [user.email],
'name': user.realname,
'auth_token_created': user.auth_token_created.strftime(format),
'auth_token_expires': user.auth_token_expires.strftime(format),
'has_credits': user.has_credits,
'enabled': user.is_active,
'groups': [g.name for g in user.groups.all()]}
response.content = json.dumps(user_info)
response['Content-Type'] = 'application/json; charset=UTF-8'
response['Content-Length'] = len(response.content)
return response
def _get_user_by_email(email):
if not email:
raise BadRequest('Email missing')
try:
user = AstakosUser.objects.get(email__iexact=email)
except AstakosUser.DoesNotExist:
raise ItemNotFound('Invalid email')
if not user.is_active:
raise ItemNotFound('Inactive user')
else:
response = HttpResponse()
response.status = 200
user_info = {'id': user.id,
'username': user.username,
'email': [user.email],
'enabled': user.is_active,
'name': user.realname,
'auth_token_created': user.auth_token_created.strftime(format),
'auth_token_expires': user.auth_token_expires.strftime(format),
'has_credits': user.has_credits,
'groups': [g.name for g in user.groups.all()],
'user_permissions': [p.codename for p in user.user_permissions.all()]}
response.content = json.dumps(user_info)
response['Content-Type'] = 'application/json; charset=UTF-8'
response['Content-Length'] = len(response.content)
return response
@api_method(http_method='GET')
def get_services(request):
callback = request.GET.get('callback', None)
......@@ -170,10 +118,6 @@ def get_menu(request, with_extra_links=False, with_signout=True):
append(item(url=absolute(request, reverse('edit_profile')),
name="My account"))
if with_extra_links:
# if user.has_usable_password() and user.provider in ('local', ''):
# append(item(
# url=absolute(request, reverse('password_change')),
# name="Change password"))
if EMAILCHANGE_ENABLED:
append(item(
url=absolute(request, reverse('email_change')),
......@@ -184,24 +128,6 @@ def get_menu(request, with_extra_links=False, with_signout=True):
name="Invitations"))
if QUOTAHOLDER_URL:
# append(item(
# url=absolute(request, reverse('group_list')),
# name="Projects",
# # submenu=(item(
# # url=absolute(request,
# # reverse('group_list')),
# # name="Overview"),
# # item(
# # url=absolute(request,
# # reverse('group_create_list')),
# # name="Create"),
# # item(
# # url=absolute(request,
# # reverse('group_search')),
# # name="Join"),
# # )
# )
# )
append(item(
url=absolute(request, reverse('project_list')),
name="Projects"))
......@@ -211,12 +137,6 @@ def get_menu(request, with_extra_links=False, with_signout=True):
append(item(
url=absolute(request, reverse('feedback')),
name="Contact"))
# append(item(
# url=absolute(request, reverse('billing')),
# name="Billing"))
# append(item(
# url=absolute(request, reverse('timeline')),
# name="Timeline"))
if with_signout:
append(item(
url=absolute(request, reverse('logout')),
......
......@@ -41,7 +41,7 @@ from smtplib import SMTPException
from astakos.im.models import (
AstakosUser,
Resource, Service, RESOURCE_SEPARATOR,
Project, ProjectApplication, ProjectMembership, filter_queryset_by_property)
Project, ProjectApplication, ProjectMembership)
from astakos.im.api.backends.base import (
BaseBackend, SuccessResult, FailureResult)
from astakos.im.api.backends.errors import (
......
......@@ -38,9 +38,11 @@ from time import time, mktime
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils import simplejson as json
from astakos.im.api.faults import Fault, Unauthorized, InternalServerError, BadRequest
from astakos.im.api import render_fault, _get_user_by_email, _get_user_by_username
from . import render_fault
from .faults import (
Fault, Unauthorized, InternalServerError, BadRequest, ItemNotFound)
from astakos.im.models import AstakosUser, Service
from astakos.im.forms import FeedbackForm
from astakos.im.functions import send_feedback as send_feedback_func
......@@ -64,8 +66,9 @@ def api_method(http_method=None, token_required=False):
service = Service.objects.get(auth_token=x_auth_token)
# Check if the token has expired.
if (time() - mktime(service.auth_token_expires.timetuple())) > 0:
raise Unauthorized('Authentication expired')
if service.auth_token_expires:
if (time() - mktime(service.auth_token_expires.timetuple())) > 0:
raise Unauthorized('Authentication expired')
except Service.DoesNotExist, e:
raise Unauthorized('Invalid X-Auth-Token')
response = func(request, *args, **kwargs)
......@@ -81,26 +84,40 @@ def api_method(http_method=None, token_required=False):
@api_method(http_method='GET', token_required=True)
def get_user_by_email(request, user=None):
def get_user_info(request):
# Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
# badRequest (400)
# unauthorised (401)
# forbidden (403)
# itemNotFound (404)
email = request.GET.get('name')
return _get_user_by_email(email)
@api_method(http_method='GET', token_required=True)
def get_user_by_username(request, user_id, user=None):
# Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
# badRequest (400)
# unauthorised (401)
# forbidden (403)
# itemNotFound (404)
return _get_user_by_username(user_id)
username = request.META.get('HTTP_X_USER_USERNAME')
uuid = request.META.get('HTTP_X_USER_UUID')
if not username and not uuid:
raise BadRequest('Either username or uuid is required.')
query = AstakosUser.objects.all()
user_info = None
if username:
try:
user = query.get(username__iexact=username)
except AstakosUser.DoesNotExist:
raise ItemNotFound('Invalid username: %s' % username)
else:
user_info = {'uuid': user.uuid}
else:
try:
user = query.get(uuid=uuid)
except AstakosUser.DoesNotExist:
raise ItemNotFound('Invalid uuid: %s' % uuid)
else:
user_info = {'username': user.username}
response = HttpResponse()
response.status = 200
response.content = json.dumps(user_info)
response['Content-Type'] = 'application/json; charset=UTF-8'
response['Content-Length'] = len(response.content)
return response
@csrf_exempt
......
......@@ -40,11 +40,6 @@ class Boolean(Integer):
Boolean = Boolean()
# class GroupKind(Integer):
# def init(self):
# self.opts.update({'minimum': 1, 'maximum': 5})
# GroupKind = GroupKind()
Timepoint = Text(classname='Timepoint', maxlen=24)
......
......@@ -39,10 +39,9 @@ from time import time, mktime
from django.http import HttpResponse
from django.utils import simplejson as json
from astakos.im.api.faults import (
Fault, Unauthorized, InternalServerError, BadRequest,
Forbidden)
from astakos.im.api import render_fault, _get_user_by_email, _get_user_by_username
from .faults import (
Fault, Unauthorized, InternalServerError, BadRequest, Forbidden)
from . import render_fault
from astakos.im.models import AstakosUser
from astakos.im.util import epoch
......@@ -84,45 +83,6 @@ def api_method(http_method=None, token_required=False, perms=None):
return decorator
@api_method(http_method='GET', token_required=True)
def authenticate_old(request, user=None):
# Normal Response Codes: 204
# Error Response Codes: internalServerError (500)
# badRequest (400)
# unauthorised (401)
if not user:
raise BadRequest('No user')
# Check if the is active.
if not user.is_active:
raise Unauthorized('User inactive')
# Check if the token has expired.
if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
raise Unauthorized('Authentication expired')
if not user.signed_terms:
raise Unauthorized('Pending approval terms')
response = HttpResponse()
response.status = 204
user_info = {
'id': user.id,
'username': user.username,
'uuid': user.uuid,
'uniq': user.email,
'auth_token': user.auth_token,
'auth_token_created': user.auth_token_created.isoformat(),
'auth_token_expires': user.auth_token_expires.isoformat(),
'has_credits': user.has_credits,
'has_signed_terms': user.signed_terms,
'groups': [g.name for g in user.groups.all()]}
response.content = json.dumps(user_info)
response['Content-Type'] = 'application/json; charset=UTF-8'
response['Content-Length'] = len(response.content)
return response
@api_method(http_method='GET', token_required=True)
def authenticate(request, user=None):
# Normal Response Codes: 204
......@@ -147,40 +107,14 @@ def authenticate(request, user=None):
response.status = 204
user_info = {
'id': user.id,
'userid': user.username,
'username': user.username,
'uuid': user.uuid,
'email': [user.email],
'name': user.realname,
'auth_token': user.auth_token,
'auth_token_created': epoch(user.auth_token_created),
'auth_token_expires': epoch(user.auth_token_expires),
'has_credits': user.has_credits,
'is_active': user.is_active,
'groups': [g.name for g in user.groups.all()]}
'has_credits': user.has_credits}
response.content = json.dumps(user_info)
response['Content-Type'] = 'application/json; charset=UTF-8'
response['Content-Length'] = len(response.content)
return response
@api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
def get_user_by_email(request, user=None):
# Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
# badRequest (400)
# unauthorised (401)
# forbidden (403)
# itemNotFound (404)
email = request.GET.get('name')
return _get_user_by_email(email)
@api_method(http_method='GET', token_required=True, perms=['im.can_access_userinfo'])
def get_user_by_username(request, user_id, user=None):
# Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
# badRequest (400)
# unauthorised (401)
# forbidden (403)
# itemNotFound (404)
return _get_user_by_username(user_id)
......@@ -65,60 +65,102 @@ def get_client():
_client = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
return _client
def call(func_name):
"""Decorator function for Quotaholder client calls."""
def decorator(payload_func):
@wraps(payload_func)
def wrapper(entities=(), **kwargs):
if not entities:
return ()
if not QUOTAHOLDER_URL:
return ()
c = get_client()
func = c.__dict__.get(func_name)
if not func:
return ()
data = payload_func(entities, **kwargs)
if not data:
return data
funcname = func.__name__
kwargs = {'context': {}, funcname: data}
rejected = func(**kwargs)
msg = _('%s: %s - Rejected: %s' % (funcname, data, rejected,))
logger.log(LOGGING_LEVEL, msg)
return rejected
return wrapper
return decorator
@call('set_quota')
def send_quota(users):
data = []
append = data.append
for user in users:
for resource, uplimit in user.quota.iteritems():
key = ENTITY_KEY
quantity = None
capacity = uplimit if uplimit != inf else None
import_limit = None
export_limit = None
flags = 0
args = (
user.uuid, resource, key, quantity, capacity, import_limit,
export_limit, flags)
append(args)
return data
def set_quota(payload):
c = get_client()
if not c:
return
result = c.set_quota(context={}, clientkey=clientkey, set_quota=payload)
logger.info('set_quota: %s rejected: %s' % (payload, result))
return result
def get_quota(user):
c = get_client()
if not c:
return
payload = []
append = payload.append
for r in user.quota.keys():
append((user.uuid, r, ENTITY_KEY),)
result = c.get_quota(context={}, clientkey=clientkey, get_quota=payload)
logger.info('get_quota: %s rejected: %s' % (payload, result))
return result
def create_entity(payload):
c = get_client()
if not c:
return
result = c.create_entity(context={}, clientkey=clientkey, create_entity=payload)
logger.info('create_entity: %s rejected: %s' % (payload, result))
return result
SetQuotaPayload = namedtuple('SetQuotaPayload', ('holder',
'resource',
'key',
'quantity',
'capacity',
'import_limit',
'export_limit',
'flags'))
GetQuotaPayload = namedtuple('GetQuotaPayload', ('holder',
'resource',
'key'))
CreateEntityPayload = namedtuple('CreateEntityPayload', ('entity',
'owner',
'key',
'ownerkey'))
QuotaLimits = namedtuple('QuotaLimits', ('holder',
'resource',
'capacity',
'import_limit',
'export_limit'))
def register_users(users):
payload = list(CreateEntityPayload(
entity=u.uuid,
owner='system',
key=ENTITY_KEY,
ownerkey='') for u in users)
rejected = create_entity(payload)
if not rejected:
payload = []
append = payload.append
for u in users:
for resource, uplimit in u.quota.iteritems():
append( SetQuotaPayload(
holder=u.uuid,
resource=resource,
key=ENTITY_KEY,
quantity=0,
capacity=uplimit if uplimit != inf else None,
import_limit=0,
export_limit=0,
flags=0))
return set_quota(payload)
def register_resources(resources):
rdata = ((r.service, r) for r in resources)
services = set(r.service for r in resources)
payload = list(CreateEntityPayload(
entity=service,
owner='system',
key=ENTITY_KEY,
ownerkey='') for service in set(services))
rejected = create_entity(payload)
if not rejected:
payload = list(SetQuotaPayload(
holder=resource.service,
resource=resource,
key=ENTITY_KEY,
quantity=None,
capacity=None,
import_limit=None,
export_limit=None,
flags=0) for resource in resources)
return set_quota(payload)
def qh_add_quota(serial, sub_list, add_list):
if not QUOTAHOLDER_URL:
return ()
......@@ -171,84 +213,6 @@ def qh_ack_serials(serials):
serials=serials)
return
@call('set_quota')
def send_resource_quantities(resources):
data = []
append = data.append
for resource in resources:
key = ENTITY_KEY
quantity = resource.meta.filter(key='quantity') or None
capacity = None
import_limit = None
export_limit = None
flags = 0
args = (resource.service.name, str(resource), key, quantity, capacity,
import_limit, export_limit, flags)
append(args)
return data
@call('get_quota')
def get_quota(users):
data = []
append = data.append
for user in users:
try:
entity = user.uuid
except AttributeError:
continue
else:
for r in user.quota.keys():
args = entity, r, ENTITY_KEY
append(args)
return data
@call('create_entity')
def create_user_entities(entities):
data = []
append = data.append
for entity in entities:
entity = entity.uuid
owner = 'system'
key = ENTITY_KEY
ownerkey = ''
args = entity, owner, key, ownerkey
append(args)
return data
@call('create_entity')
def create_service_entities(entities):
data = []
append = data.append
l = []
for entity in entities:
entity = entity.service.name
if entity in l:
continue
l.append(entity)
owner = 'system'
key = ENTITY_KEY
ownerkey = ''
args = entity, owner, key, ownerkey
append(args)
return data
def register_users(users):
users, copy = itertools.tee(users)
rejected = create_user_entities(entities=users)
created = (e for e in copy if unicode(e) not in rejected)
return send_quota(created)
def register_resources(resources):
resources, copy = itertools.tee(resources)
rejected = create_service_entities(entities=resources)
created = (e for e in copy if unicode(e) not in rejected)
return send_resource_quantities(created)
from datetime import datetime
strptime = datetime.strptime
......
......@@ -637,6 +637,7 @@ class ProjectApplicationForm(forms.ModelForm):
help_text=" The Project's name should be in a domain format. The domain shouldn't neccessarily exist in the real world but is helpful to imply a structure. e.g.: myproject.mylab.ntua.gr or myservice.myteam.myorganization "
)
homepage = forms.URLField(
label="Homepage Url",
help_text="This should be a URL pointing at your project's site. e.g.: http://myproject.com ",
widget=forms.TextInput(attrs={'placeholder': 'http://myproject.com'}),
......
......@@ -65,8 +65,8 @@ from astakos.im.settings import (
from astakos.im.notifications import build_notification, NotificationError
from astakos.im.models import (
AstakosUser, ProjectMembership, ProjectApplication, Project,
trigger_sync, get_closed_join, get_auto_accept_join,
get_auto_accept_leave, get_closed_leave)
MemberLeavePolicy, MemberJoinPolicy,
trigger_sync)
import astakos.im.messages as astakos_messages
......@@ -366,6 +366,7 @@ class SendNotificationError(SendMailError):
### PROJECT VIEWS ###
def get_join_policy(str_policy):
try:
return MemberJoinPolicy.objects.get(policy=str_policy)
......@@ -375,37 +376,37 @@ def get_join_policy(str_policy):
def get_leave_policy(str_policy):
try:
return MemberLeavePolicy.objects.get(policy=str_policy)
except:
except BaseException, e:
return None
_auto_accept_join = False
_auto_accept_join = None
def get_auto_accept_join_policy():
global _auto_accept_join
if _auto_accept_join is not False:
if _auto_accept_join is not None:
return _auto_accept_join
_auto_accept = get_join_policy('auto_accept')
return _auto_accept
_closed_join = False
_closed_join = None