Commit b3763c68 authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

Use common 'api_method' decorator to astakos api

Update all astakos API methods to use the new common 'api_method'
decorator. The 'api_method' decorator 'user_required' option
authenticates uses by their token through astakos. Consequently,
astakos methods can not use this option. Instead, two custom
decorator are implemented for astakos:
* user_from_token: Asserts that a user with the specified X-Auth-Token
  exists
* service_from_token: Asserts that a service with the specified
* X-Auth-Token exists.
parent ee91464b
# Copyright 2011-2012 GRNET S.A. All rights reserved.
# Copyright 2011-2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
......@@ -31,18 +31,16 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from functools import wraps
from traceback import format_exc
from urllib import quote, unquote
from functools import partial
from django.http import HttpResponse
from django.utils import simplejson as json
from django.conf import settings
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from django.contrib import messages
from astakos.im.models import AstakosUser, Service, Resource
from astakos.im.models import AstakosUser, Service
from snf_django.lib import api
from snf_django.lib.api import faults
from astakos.im.settings import (
INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED, QUOTAHOLDER_URL,
......@@ -58,44 +56,17 @@ format = ('%a, %d %b %Y %H:%M:%S GMT')
absolute = lambda request, url: request.build_absolute_uri(url)
def render_fault(request, fault):
if isinstance(fault, faults.InternalServerError) and settings.DEBUG:
fault.details = format_exc(fault)
request.serialization = 'text'
data = fault.message + '\n'
if fault.details:
data += '\n' + fault.details
response = HttpResponse(data, status=fault.code)
response['Content-Length'] = len(response.content)
return response
def api_method(http_method=None):
"""Decorator function for views that implement an API method."""
def decorator(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
try:
if http_method and request.method != http_method:
raise faults.BadRequest('Method not allowed.')
response = func(request, *args, **kwargs)
return response
except faults.Fault, fault:
return render_fault(request, fault)
except BaseException, e:
logger.exception('Unexpected error: %s' % e)
fault = faults.InternalServerError('Unexpected error')
return render_fault(request, fault)
return wrapper
return decorator
# Decorator for API methods, using common utils.api_method decorator.
# It is used for 'get_services' and 'get_menu' methods that do not
# require any sort of authentication
api_method = partial(api.api_method, user_required=False,
token_required=False, logger=logger)
def get_services_dict():
services = Service.objects.all()
data = tuple({'id': s.pk, 'name': s.name, 'url': s.url, 'icon':
s.icon} for s in services)
return data
"""Return dictionary with information about available Services."""
return list(Service.objects.values("id", "name", "url", "icon"))
@api_method(http_method=None)
def get_services(request):
......@@ -122,55 +93,48 @@ def get_services(request):
@api_method()
def get_menu(request, with_extra_links=False, with_signout=True):
user = request.user
from_location = request.GET.get('location')
index_url = reverse('index')
l = [{'url': absolute(request, index_url), 'name': _("Sign in")}]
if user.is_authenticated():
l = []
append = l.append
item = MenuItem
item.current_path = absolute(request, request.path)
append(item(
url=absolute(request, reverse('index')),
name=user.email))
append(item(url=absolute(request, reverse('index')),
name=user.email))
if with_extra_links:
append(item(
url=absolute(request, reverse('landing')),
name="Overview"))
append(item(url=absolute(request, reverse('landing')),
name="Overview"))
if with_signout:
append(item(
url=absolute(request, reverse('edit_profile')),
name="Dashboard"))
append(item(url=absolute(request, reverse('edit_profile')),
name="Dashboard"))
if with_extra_links:
append(item(url=absolute(request, reverse('edit_profile')),
name="Profile"))
name="Profile"))
if with_extra_links:
if INVITATIONS_ENABLED:
append(item(
url=absolute(request, reverse('invite')),
name="Invitations"))
append(item(url=absolute(request, reverse('invite')),
name="Invitations"))
append(item(url=absolute(request, reverse('resource_usage')),
name="Usage"))
append(item(
url=absolute(request, reverse('resource_usage')),
name="Usage"))
if QUOTAHOLDER_URL and PROJECTS_VISIBLE:
append(item(
url=absolute(request, reverse('project_list')),
name="Projects"))
append(item(url=absolute(request, reverse('project_list')),
name="Projects"))
#append(item(
#url=absolute(request, reverse('api_access')),
#name="API Access"))
append(item(
url=absolute(request, reverse('feedback')),
name="Contact"))
append(item(url=absolute(request, reverse('feedback')),
name="Contact"))
if with_signout:
append(item(
url=absolute(request, reverse('logout')),
name="Sign out"))
append(item(url=absolute(request, reverse('logout')),
name="Sign out"))
else:
l = [{'url': absolute(request, index_url),
'name': _("Sign in")}]
callback = request.GET.get('callback', None)
data = json.dumps(tuple(l))
......@@ -217,6 +181,7 @@ class MenuItem(dict):
if name == 'current_path':
self.__set_is_active__()
def __get_uuid_displayname_catalogs(request, user_call=True):
# Normal Response Codes: 200
# Error Response Codes: BadRequest (400)
......@@ -232,8 +197,9 @@ def __get_uuid_displayname_catalogs(request, user_call=True):
displaynames = input_data.get('displaynames', [])
if displaynames == None and user_call:
displaynames = []
d = {'uuid_catalog':AstakosUser.objects.uuid_catalog(uuids),
'displayname_catalog':AstakosUser.objects.displayname_catalog(displaynames)}
user_obj = AstakosUser.objects
d = {'uuid_catalog': user_obj.uuid_catalog(uuids),
'displayname_catalog': user_obj.displayname_catalog(displaynames)}
response = HttpResponse()
response.content = json.dumps(d)
......@@ -241,7 +207,9 @@ def __get_uuid_displayname_catalogs(request, user_call=True):
response['Content-Length'] = len(response.content)
return response
def __send_feedback(request, email_template_name='im/feedback_mail.txt', user=None):
def __send_feedback(request, email_template_name='im/feedback_mail.txt',
user=None):
if not user:
auth_token = request.POST.get('auth', '')
if not auth_token:
......
......@@ -31,70 +31,71 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import logging
from functools import wraps
from time import time, mktime
from functools import wraps
from django.views.decorators.csrf import csrf_exempt
from . import render_fault, __get_uuid_displayname_catalogs, __send_feedback
from . import __get_uuid_displayname_catalogs, __send_feedback
from snf_django.lib import api
from snf_django.lib.api import faults
from astakos.im.models import Service
import logging
logger = logging.getLogger(__name__)
def api_method(http_method=None, token_required=False):
"""Decorator function for views that implement an API method."""
def decorator(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
try:
if http_method and request.method != http_method:
raise faults.BadRequest('Method not allowed.')
x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN')
if token_required:
if not x_auth_token:
raise faults.Unauthorized('Access denied')
try:
service = Service.objects.get(auth_token=x_auth_token)
def service_from_token(func):
"""Decorator for authenticating service by it's token.
# Check if the token has expired.
if service.auth_token_expires:
if (time() - mktime(service.auth_token_expires.timetuple())) > 0:
raise faults.Unauthorized('Authentication expired')
except Service.DoesNotExist, e:
raise faults.Unauthorized('Invalid X-Auth-Token')
response = func(request, *args, **kwargs)
return response
except faults.Fault, fault:
return render_fault(request, fault)
except BaseException, e:
logger.exception('Unexpected error: %s' % e)
fault = faults.InternalServerError('Unexpected error')
return render_fault(request, fault)
return wrapper
return decorator
Check that a service with the corresponding token exists. Also,
if service's token has an expiration token, check that it has not
expired.
"""
@wraps(func)
def wrapper(request, *args, **kwargs):
try:
token = request.x_auth_token
except AttributeError:
raise faults.Unauthorized("No authentication token")
if not token:
raise faults.Unauthorized("Invalid X-Auth-Token")
try:
service = Service.objects.get(auth_token=token)
except Service.DoesNotExist:
raise faults.Unauthorized("Invalid X-Auth-Token")
# Check if the token has expired
expiration_date = service.auth_token_expires
if expiration_date:
expires_at = mktime(expiration_date.timetuple())
if time() > expires_at:
raise faults.Unauthorized("Authentication expired")
return func(request, *args, **kwargs)
return wrapper
@csrf_exempt
@api_method(http_method='POST', token_required=True)
@api.api_method(http_method='POST', token_required=True, user_required=False,
logger=logger)
@service_from_token # Authenticate service !!
def get_uuid_displayname_catalogs(request):
# Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
# badRequest (400)
# unauthorised (401)
return __get_uuid_displayname_catalogs(request, user_call=False)
@csrf_exempt
@api_method(http_method='POST', token_required=True)
@api.api_method(http_method='POST', token_required=True, user_required=False,
logger=logger)
@service_from_token # Authenticate service !!
def send_feedback(request, email_template_name='im/feedback_mail.txt'):
# Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
# badRequest (400)
# unauthorised (401)
return __send_feedback(request, email_template_name)
......@@ -31,8 +31,6 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
import logging
from functools import wraps
from time import time, mktime
......@@ -40,8 +38,9 @@ from django.http import HttpResponse
from django.utils import simplejson as json
from django.views.decorators.csrf import csrf_exempt
from snf_django.lib import api
from snf_django.lib.api import faults
from . import render_fault, __get_uuid_displayname_catalogs, __send_feedback
from . import __get_uuid_displayname_catalogs, __send_feedback
from astakos.im.models import AstakosUser
from astakos.im.util import epoch
......@@ -49,45 +48,34 @@ from astakos.im.util import epoch
from astakos.im.api.callpoint import AstakosCallpoint
callpoint = AstakosCallpoint()
import logging
logger = logging.getLogger(__name__)
format = ('%a, %d %b %Y %H:%M:%S GMT')
def api_method(http_method=None, token_required=False, perms=None):
"""Decorator function for views that implement an API method."""
if not perms:
perms = []
def decorator(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
try:
if http_method and request.method != http_method:
raise faults.BadRequest('Method not allowed.')
x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN')
if token_required:
if not x_auth_token:
raise faults.Unauthorized('Access denied')
try:
user = AstakosUser.objects.get(auth_token=x_auth_token)
if not user.has_perms(perms):
raise faults.Forbidden('Unauthorized request')
except AstakosUser.DoesNotExist, e:
raise faults.Unauthorized('Invalid X-Auth-Token')
kwargs['user'] = user
response = func(request, *args, **kwargs)
return response
except faults.Fault, fault:
return render_fault(request, fault)
except BaseException, e:
logger.exception('Unexpected error: %s' % e)
fault = faults.InternalServerError('Unexpected error')
return render_fault(request, fault)
return wrapper
return decorator
@api_method(http_method='GET', token_required=True)
def user_from_token(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
try:
token = request.x_auth_token
except AttributeError:
raise faults.Unauthorized("No authentication token")
if not token:
raise faults.Unauthorized("Invalid X-Auth-Token")
try:
user = AstakosUser.objects.get(auth_token=token)
except AstakosUser.DoesNotExist:
raise faults.Unauthorized('Invalid X-Auth-Token')
return func(request, user, *args, **kwargs)
return wrapper
@api.api_method(http_method="GET", token_required=True, user_required=False,
logger=logger)
@user_from_token # Authenticate user!!
def authenticate(request, user=None):
# Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
......@@ -136,7 +124,9 @@ def authenticate(request, user=None):
@csrf_exempt
@api_method(http_method='POST', token_required=True)
@api.api_method(http_method="POST", token_required=True, user_required=False,
logger=logger)
@user_from_token # Authenticate user!!
def get_uuid_displayname_catalogs(request, user=None):
# Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
......@@ -147,7 +137,9 @@ def get_uuid_displayname_catalogs(request, user=None):
@csrf_exempt
@api_method(http_method='POST', token_required=True)
@api.api_method(http_method="POST", token_required=True, user_required=False,
logger=logger)
@user_from_token # Authenticate user!!
def send_feedback(request, email_template_name='im/feedback_mail.txt',
user=None):
# Normal Response Codes: 200
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment