Revision a24b5bda
b/docs/astakos-api-guide.rst | ||
---|---|---|
20 | 20 |
========================= ================================ |
21 | 21 |
Revision Description |
22 | 22 |
========================= ================================ |
23 |
0.14 (June 03, 2013) Remove endpoint listing |
|
23 | 24 |
0.14 (May 28, 2013) Extend token api with authenticate call |
24 | 25 |
0.14 (May 23, 2013) Extend api to list endpoints |
25 | 26 |
0.14 (May 14, 2013) Do not serve user quotas in :ref:`authenticate-api-label` |
... | ... | |
474 | 475 |
401 (Unauthorized) Invalid token or invalid creadentials or tenantName does not comply with the provided token |
475 | 476 |
500 (Internal Server Error) The request cannot be completed because of an internal error |
476 | 477 |
=========================== ===================== |
477 |
|
|
478 |
|
|
479 |
|
|
480 |
Get endpoints |
|
481 |
^^^^^^^^^^^^^ |
|
482 |
|
|
483 |
Return a json (or xml) formatted dictionary containing information about registered endpoints |
|
484 |
|
|
485 |
========================================= ========= ================== |
|
486 |
Uri Method Description |
|
487 |
========================================= ========= ================== |
|
488 |
``/astakos/api/tokens/<token>/endpoints`` GET Returns a list registered endpoints |
|
489 |
========================================= ========= ================== |
|
490 |
|
|
491 |
| |
|
492 |
|
|
493 |
==================== ============================ |
|
494 |
Request Header Name Value |
|
495 |
==================== ============================ |
|
496 |
X-Auth-Token User authentication token |
|
497 |
==================== ============================ |
|
498 |
|
|
499 |
| |
|
500 |
|
|
501 |
====================== ============================ |
|
502 |
Request Parameter Name Value |
|
503 |
|
|
504 |
====================== ============================ |
|
505 |
belongsTo Check that the token belongs to a supplied user |
|
506 |
marker Return endpoints (ordered by ID) whose ID is higher than the marker |
|
507 |
limit Maximum number of endpoints to return |
|
508 |
====================== ============================ |
|
509 |
|
|
510 |
| |
|
511 |
|
|
512 |
Example json reply: |
|
513 |
|
|
514 |
:: |
|
515 |
|
|
516 |
{"endpoints": [ |
|
517 |
{"name": "cyclades", |
|
518 |
"region": "cyclades", |
|
519 |
"internalURL": "https://node1.example.com/v1", |
|
520 |
"adminURL": "https://node1.example.com/v1", |
|
521 |
"type": null, |
|
522 |
"id": 5, |
|
523 |
"publicURL": "https://node1.example.com/vi/"}, |
|
524 |
{"name": "pithos", |
|
525 |
"region": "pithos", |
|
526 |
"internalURL": "https://node2.example.com/vi/", |
|
527 |
"adminURL": "https://node2.example.com/v1", |
|
528 |
"type": null, |
|
529 |
"id": 6, |
|
530 |
"publicURL": "https://node2.example.com/vi/"}, |
|
531 |
], |
|
532 |
"endpoint_links": [{ |
|
533 |
"href": "/astakos/api/tokens/0000/endpoints?marker=6&limit=10000", |
|
534 |
"rel": "next"}]} |
|
535 |
|
|
536 |
|
|
537 |
Example xml reply: |
|
538 |
|
|
539 |
:: |
|
540 |
|
|
541 |
<?xml version="1.0" encoding="UTF-8"?> |
|
542 |
<endpoints xmlns="http://docs.openstack.org/identity/api/v2.0"> |
|
543 |
<endpoint "name"="cyclades" "region"="cyclades" "internalURL"="https://node1.example.com/ui/" "adminURL"="https://node1.example.com/ui/" "id"="5" "publicURL"="https://node1.example.com/ui/" /> |
|
544 |
<endpoint "name"="pithos" "region"="pithos" "internalURL"="https://node2.example.com/ui/" "adminURL"="https://node2.example.com/v1" "id"="6" "publicURL"="https://node2.example.com/ui/" /> |
|
545 |
</endpoints> |
|
546 |
<endpoint_links> |
|
547 |
<endpoint_link "href"="/astakos/api/tokens/0000/endpoints?marker=6&limit=10000" "rel"="next" /> |
|
548 |
</endpoint_links> |
|
549 |
|
|
550 |
|
|
551 |
| |
|
552 |
|
|
553 |
=========================== ===================== |
|
554 |
Return Code Description |
|
555 |
=========================== ===================== |
|
556 |
200 (OK) The request succeeded |
|
557 |
400 (Bad Request) Method not allowed or token does not belong to the specific user |
|
558 |
401 (Unauthorized) Missing or expired or invalid service token |
|
559 |
403 (Forbidden) Path token does not comply with X-Auth-Token |
|
560 |
500 (Internal Server Error) The request cannot be completed because of an internal error |
|
561 |
=========================== ===================== |
b/snf-astakos-app/astakos/api/keystone_urls.py | ||
---|---|---|
36 | 36 |
urlpatterns = patterns( |
37 | 37 |
'astakos.api.tokens', |
38 | 38 |
url(r'tokens/?$', 'authenticate'), |
39 |
url(r'tokens/(?P<token>.+?)/endpoints', 'get_endpoints'), |
|
40 | 39 |
) |
b/snf-astakos-app/astakos/api/tokens.py | ||
---|---|---|
31 | 31 |
# interpreted as representing official policies, either expressed |
32 | 32 |
# or implied, of GRNET S.A. |
33 | 33 |
|
34 |
from urlparse import urlunsplit, urlsplit |
|
35 | 34 |
from collections import defaultdict |
36 | 35 |
|
37 |
from django.http import urlencode |
|
38 | 36 |
from django.views.decorators.csrf import csrf_exempt |
39 | 37 |
|
40 | 38 |
from snf_django.lib.api import faults, utils, api_method |
41 | 39 |
|
42 | 40 |
from astakos.im.models import Service, AstakosUser |
43 |
from .util import user_from_token, json_response, xml_response, validate_user
|
|
41 |
from .util import json_response, xml_response, validate_user |
|
44 | 42 |
|
45 | 43 |
import logging |
46 | 44 |
logger = logging.getLogger(__name__) |
47 | 45 |
|
48 | 46 |
|
49 |
@api_method(http_method="GET", token_required=True, user_required=False, |
|
50 |
logger=logger) |
|
51 |
@user_from_token # Authenticate user!! |
|
52 |
def get_endpoints(request, token): |
|
53 |
if token != request.user.auth_token: |
|
54 |
raise faults.Forbidden() |
|
55 |
|
|
56 |
belongsTo = request.GET.get('belongsTo') |
|
57 |
if belongsTo and belongsTo != request.user.uuid: |
|
58 |
raise faults.BadRequest() |
|
59 |
|
|
60 |
marker = request.GET.get('marker', 0) |
|
61 |
limit = request.GET.get('limit', 10000) |
|
62 |
|
|
63 |
endpoints = list(Service.objects.all().order_by('id'). |
|
64 |
filter(id__gt=marker)[:limit]. |
|
65 |
values('name', 'url', 'api_url', 'id', 'type')) |
|
66 |
for e in endpoints: |
|
67 |
e['publicURL'] = e['admiURL'] = e['internalURL'] = e['api_url'] |
|
68 |
e['SNF:uiURL'] = e['url'] |
|
69 |
e['region'] = e['name'] |
|
70 |
e.pop('api_url') |
|
71 |
|
|
72 |
if endpoints: |
|
73 |
parts = list(urlsplit(request.path)) |
|
74 |
params = {'marker': endpoints[-1]['id'], 'limit': limit} |
|
75 |
parts[3] = urlencode(params) |
|
76 |
next_page_url = urlunsplit(parts) |
|
77 |
endpoint_links = [{'href': next_page_url, 'rel': 'next'}] |
|
78 |
else: |
|
79 |
endpoint_links = [] |
|
80 |
|
|
81 |
result = {'endpoints': endpoints, 'endpoint_links': endpoint_links} |
|
82 |
if request.serialization == 'xml': |
|
83 |
return xml_response(result, 'api/endpoints.xml') |
|
84 |
else: |
|
85 |
return json_response(result) |
|
86 |
|
|
87 |
|
|
88 | 47 |
@csrf_exempt |
89 | 48 |
@api_method(http_method="POST", token_required=False, user_required=False, |
90 | 49 |
logger=logger) |
b/snf-astakos-app/astakos/im/tests/api.py | ||
---|---|---|
36 | 36 |
from django.test import TestCase |
37 | 37 |
from django.core.urlresolvers import reverse |
38 | 38 |
|
39 |
|
|
40 |
from urllib import quote |
|
41 |
from urlparse import urlparse, parse_qs |
|
42 | 39 |
#from xml.dom import minidom |
43 | 40 |
|
44 | 41 |
import json |
... | ... | |
553 | 550 |
# body = minidom.parseString(r.content) |
554 | 551 |
# except Exception, e: |
555 | 552 |
# self.fail(e) |
556 |
|
|
557 |
def test_get_endpoints(self): |
|
558 |
client = Client() |
|
559 |
|
|
560 |
# Check in active user token |
|
561 |
inactive_user = AstakosUser.objects.create(email='test3') |
|
562 |
url = '/astakos/api/tokens/%s/endpoints' % quote( |
|
563 |
inactive_user.auth_token) |
|
564 |
r = client.get(url) |
|
565 |
self.assertEqual(r.status_code, 401) |
|
566 |
|
|
567 |
# Check invalid user token in path |
|
568 |
url = '/astakos/api/tokens/nouser/endpoints' |
|
569 |
r = client.get(url) |
|
570 |
self.assertEqual(r.status_code, 401) |
|
571 |
|
|
572 |
# Check forbidden |
|
573 |
url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token) |
|
574 |
headers = {'HTTP_X_AUTH_TOKEN': AstakosUser.objects.create( |
|
575 |
email='test4').auth_token} |
|
576 |
r = client.get(url, **headers) |
|
577 |
self.assertEqual(r.status_code, 401) |
|
578 |
|
|
579 |
# Check bad request method |
|
580 |
url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token) |
|
581 |
r = client.post(url) |
|
582 |
self.assertEqual(r.status_code, 400) |
|
583 |
|
|
584 |
# Check forbidden |
|
585 |
url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token) |
|
586 |
headers = {'HTTP_X_AUTH_TOKEN': self.user2.auth_token} |
|
587 |
r = client.get(url, **headers) |
|
588 |
self.assertEqual(r.status_code, 403) |
|
589 |
|
|
590 |
# Check belongsTo BadRequest |
|
591 |
url = '/astakos/api/tokens/%s/endpoints?belongsTo=%s' % ( |
|
592 |
quote(self.user1.auth_token), quote(self.user2.uuid)) |
|
593 |
headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token} |
|
594 |
r = client.get(url, **headers) |
|
595 |
self.assertEqual(r.status_code, 400) |
|
596 |
|
|
597 |
# Check successful request |
|
598 |
url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token) |
|
599 |
headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token} |
|
600 |
r = client.get(url, **headers) |
|
601 |
self.assertEqual(r.status_code, 200) |
|
602 |
self.assertEqual(r['Content-Type'], 'application/json; charset=UTF-8') |
|
603 |
try: |
|
604 |
body = json.loads(r.content) |
|
605 |
except: |
|
606 |
self.fail('json format expected') |
|
607 |
endpoints = body.get('endpoints') |
|
608 |
self.assertEqual(len(endpoints), 3) |
|
609 |
|
|
610 |
# Check xml serialization |
|
611 |
url = '/astakos/api/tokens/%s/endpoints?format=xml' %\ |
|
612 |
quote(self.user1.auth_token) |
|
613 |
headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token} |
|
614 |
r = client.get(url, **headers) |
|
615 |
self.assertEqual(r.status_code, 200) |
|
616 |
self.assertEqual(r['Content-Type'], 'application/xml; charset=UTF-8') |
|
617 |
# try: |
|
618 |
# body = minidom.parseString(r.content) |
|
619 |
# except Exception, e: |
|
620 |
# self.fail('xml format expected') |
|
621 |
endpoints = body.get('endpoints') |
|
622 |
self.assertEqual(len(endpoints), 3) |
|
623 |
|
|
624 |
# Check limit |
|
625 |
url = '/astakos/api/tokens/%s/endpoints?limit=2' %\ |
|
626 |
quote(self.user1.auth_token) |
|
627 |
headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token} |
|
628 |
r = client.get(url, **headers) |
|
629 |
self.assertEqual(r.status_code, 200) |
|
630 |
body = json.loads(r.content) |
|
631 |
endpoints = body.get('endpoints') |
|
632 |
self.assertEqual(len(endpoints), 2) |
|
633 |
|
|
634 |
endpoint_link = body.get('endpoint_links', [])[0] |
|
635 |
next = endpoint_link.get('href') |
|
636 |
p = urlparse(next) |
|
637 |
params = parse_qs(p.query) |
|
638 |
self.assertTrue('limit' in params) |
|
639 |
self.assertTrue('marker' in params) |
|
640 |
self.assertEqual(params['marker'][0], '2') |
|
641 |
|
|
642 |
# Check marker |
|
643 |
headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token} |
|
644 |
r = client.get(next, **headers) |
|
645 |
self.assertEqual(r.status_code, 200) |
|
646 |
body = json.loads(r.content) |
|
647 |
endpoints = body.get('endpoints') |
|
648 |
self.assertEqual(len(endpoints), 1) |
Also available in: Unified diff