Commit a0b9f620 authored by Sofia Papagiannaki's avatar Sofia Papagiannaki

astakos: provide get_endpoints api call

parent c8ada5c2
# 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
# 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 urlparse import urlunsplit, urlsplit
from django.http import urlencode
from snf_django.lib import api
from astakos.im.models import Service
from .util import user_from_token, rename_meta_key, json_response, xml_response
import logging
logger = logging.getLogger(__name__)
@api.api_method(http_method="GET", token_required=True, user_required=False,
logger=logger)
@user_from_token # Authenticate user!!
def get_endpoints(request, token):
if token != api.get_token(request):
raise api.faults.Forbidden()
belongsTo = request.GET.get('belongsTo')
if belongsTo and belongsTo != request.user.uuid:
raise api.faults.BadRequest()
marker = request.GET.get('marker', 0)
limit = request.GET.get('limit', 10000)
endpoints = list(Service.objects.all().order_by('id').\
filter(id__gt=marker)[:limit].\
values('name', 'url', 'api_url', 'id', 'type'))
for e in endpoints:
e['api_url'] = e['api_url'] or e['url']
e['internalURL'] = e['url']
e['region'] = e['name']
rename_meta_key(e, 'api_url', 'adminURL')
rename_meta_key(e, 'url', 'publicURL')
if endpoints:
parts = list(urlsplit(request.path))
params = {'marker': endpoints[-1]['id'], 'limit': limit}
parts[3] = urlencode(params)
next_page_url = urlunsplit(parts)
endpoint_links = [{'href': next_page_url, 'rel': 'next'}]
else:
endpoint_links = []
result = {'endpoints': endpoints, 'endpoint_links': endpoint_links}
if request.serialization == 'xml':
return xml_response(result, 'api/endpoints.xml')
else:
return json_response(result)
......@@ -56,3 +56,8 @@ urlpatterns += patterns(
'astakos.api.service',
url(r'^service/user_catalogs/?$', 'get_uuid_displayname_catalogs'),
)
urlpatterns += patterns(
'astakos.api.tokens',
url(r'tokens/(?P<token>.+?)/endpoints', 'get_endpoints'),
)
......@@ -36,6 +36,7 @@ from time import time, mktime
from django.http import HttpResponse
from django.utils import simplejson as json
from django.template.loader import render_to_string
from astakos.im.models import AstakosUser, Service
from snf_django.lib.api import faults
......@@ -60,6 +61,17 @@ def json_response(content, status_code=None):
return response
def xml_response(content, template, status_code=None):
response = HttpResponse()
if status_code is not None:
response.status_code = status_code
response.content = render_to_string(template, content)
response['Content-Type'] = 'application/xml; charset=UTF-8'
response['Content-Length'] = len(response.content)
return response
def is_integer(x):
return isinstance(x, (int, long))
......@@ -174,3 +186,10 @@ def send_feedback(request, email_template_name='im/feedback_mail.txt'):
except:
return HttpResponse(status=502)
return HttpResponse(status=200)
def rename_meta_key(d, old, new):
if old not in d:
return
d[new] = d[old]
del(d[old])
......@@ -31,7 +31,10 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from django.db.utils import IntegrityError
from astakos.im.models import Service
......@@ -40,29 +43,32 @@ class Command(BaseCommand):
args = "<name> <service URL> <API URL> "
help = "Register a service"
option_list = BaseCommand.option_list + (
make_option('--type',
dest='type',
help="Service type"),
)
def handle(self, *args, **options):
if len(args) < 2:
raise CommandError("Invalid number of arguments")
name = args[0]
api_url = args[1]
url = args[2]
try:
s = Service.objects.get(name=name)
m = "There already exists service named '%s'." % name
raise CommandError(m)
except Service.DoesNotExist:
pass
kwargs = dict(name=args[0], api_url=args[1], url=args[2])
if options['type']:
kwargs['type'] = options['type']
services = list(Service.objects.filter(api_url=api_url))
services = list(Service.objects.filter(api_url=kwargs['api_url']))
if services:
m = "URL '%s' is registered for another service." % api_url
m = "URL '%s' is registered for another service." %\
kwargs['api_url']
raise CommandError(m)
try:
s = Service.objects.create(name=name, api_url=api_url, url=url)
except BaseException as e:
s = Service.objects.create(**kwargs)
except IntegrityError:
m = "There already exists service named '%s'." % kwargs['name']
raise CommandError(m)
except BaseException:
raise CommandError("Failed to create service.")
else:
self.stdout.write('Token: %s\n' % s.auth_token)
......@@ -47,6 +47,7 @@ class Command(ListCommand):
"token": ("auth_token", "Authentication token"),
"created": ("auth_token_created", "Token creation date"),
"expires": ("auth_token_expires", "Token expiration date"),
"type": ("type", "Service type"),
}
fields = ["id", "name", "url", "api_url", "token", "created", "expires"]
......@@ -64,6 +64,10 @@ class Command(BaseCommand):
dest='renew_token',
default=False,
help="Renew service auth token"),
make_option('--type',
dest='type',
default=None,
help="Modify service type"),
)
def handle(self, *args, **options):
......@@ -81,6 +85,7 @@ class Command(BaseCommand):
url = options.get('url')
auth_token = options.get('auth_token')
renew_token = options.get('renew_token')
type = options.get('type')
if name:
service.name = name
......@@ -97,6 +102,9 @@ class Command(BaseCommand):
if renew_token and not auth_token:
service.renew_token()
if type:
service.type = type
service.save()
if renew_token:
......
......@@ -99,6 +99,7 @@ class Service(models.Model):
url = models.CharField(_('Service url'), max_length=255, null=True,
help_text=_("URL the service is accessible from"))
api_url = models.CharField(_('Service API url'), max_length=255, null=True)
type = models.CharField(_('Type'), max_length=255, null=True, blank='True')
auth_token = models.CharField(_('Authentication Token'), max_length=32,
null=True, blank=True)
auth_token_created = models.DateTimeField(_('Token creation date'),
......
<?xml version="1.0" encoding="UTF-8"?>
{% load get_type %}
<endpoints xmlns="http://docs.openstack.org/identity/api/v2.0">
{% for e in endpoints %}
<endpoint {%for k,v in e.items %}{% if v %}"{{k}}"="{{v}}" {% endif %}{%endfor%}/>
{% endfor %}
</endpoints>
<endpoint_links>
{% for l in endpoint_links %}
<endpoint_link {%for k,v in l.items %}"{{k}}"="{{v}}" {%endfor%}/>
{% endfor %}
</endpoint_links>
......@@ -32,8 +32,15 @@
# or implied, of GRNET S.A.
from astakos.im.tests.common import *
from django.test import TestCase
from urllib import quote
from urlparse import urlparse, parse_qs
#from xml.dom import minidom
import json
ROOT = '/astakos/api/'
u = lambda url: ROOT + url
......@@ -361,3 +368,94 @@ class QuotaAPITest(TestCase):
r11 = system_quota[resource11['name']]
self.assertEqual(r11['usage'], 102)
self.assertEqual(r11['pending'], 101)
class TokensApiTest(TestCase):
def setUp(self):
self.user1 = AstakosUser.objects.create(email='test1', is_active=True)
self.user2 = AstakosUser.objects.create(email='test2', is_active=True)
Service(name='service1', url='http://localhost/service1',
api_url='http://localhost/api/service1').save()
Service(name='service2', url='http://localhost/service2',
api_url='http://localhost/api/service2').save()
Service(name='service3', url='http://localhost/service3',
api_url='http://localhost/api/service3').save()
def test_get_endpoints(self):
client = Client()
# Check unauthorized request
url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token)
r = client.get(url)
self.assertEqual(r.status_code, 401)
# Check bad request method
url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token)
r = client.post(url)
self.assertEqual(r.status_code, 400)
# Check forbidden
url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token)
headers = {'HTTP_X_AUTH_TOKEN': self.user2.auth_token}
r = client.get(url, **headers)
self.assertEqual(r.status_code, 403)
url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token)
headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token}
r = client.get(url, **headers)
self.assertEqual(r.status_code, 200)
self.assertEqual(r['Content-Type'], 'application/json; charset=UTF-8')
try:
body = json.loads(r.content)
except:
self.fail('json format expected')
endpoints = body.get('endpoints')
self.assertEqual(len(endpoints), 3)
# Check belongsTo BadRequest
url = '/astakos/api/tokens/%s/endpoints?belongsTo=%s' % (
quote(self.user1.auth_token), quote(self.user2.uuid))
headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token}
r = client.get(url, **headers)
self.assertEqual(r.status_code, 400)
# Check xml serialization
url = '/astakos/api/tokens/%s/endpoints?format=xml' %\
quote(self.user1.auth_token)
headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token}
r = client.get(url, **headers)
self.assertEqual(r.status_code, 200)
self.assertEqual(r['Content-Type'], 'application/xml; charset=UTF-8')
# try:
# body = minidom.parseString(r.content)
# except Exception, e:
# self.fail('xml format expected')
endpoints = body.get('endpoints')
self.assertEqual(len(endpoints), 3)
# Check limit
url = '/astakos/api/tokens/%s/endpoints?limit=2' %\
quote(self.user1.auth_token)
headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token}
r = client.get(url, **headers)
self.assertEqual(r.status_code, 200)
body = json.loads(r.content)
endpoints = body.get('endpoints')
self.assertEqual(len(endpoints), 2)
endpoint_link = body.get('endpoint_links', [])[0]
next = endpoint_link.get('href')
p = urlparse(next)
params = parse_qs(p.query)
self.assertTrue('limit' in params)
self.assertTrue('marker' in params)
self.assertEqual(params['marker'][0], '2')
# Check marker
headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token}
r = client.get(next, **headers)
self.assertEqual(r.status_code, 200)
body = json.loads(r.content)
endpoints = body.get('endpoints')
self.assertEqual(len(endpoints), 1)
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