Revision f870efe2

b/snf-astakos-app/astakos/api/tokens.py
34 34
from urlparse import urlunsplit, urlsplit
35 35

  
36 36
from django.http import urlencode
37
from django.views.decorators.csrf import csrf_exempt
37 38

  
38
from snf_django.lib import api
39
from snf_django.lib.api import faults, utils, api_method, get_token
39 40

  
40
from astakos.im.models import Service
41

  
42
from .util import user_from_token, rename_meta_key, json_response, xml_response
41
from astakos.im.models import Service, AstakosUser
42
from .util import user_from_token, json_response, xml_response
43 43

  
44 44
import logging
45 45
logger = logging.getLogger(__name__)
46 46

  
47 47

  
48
@api.api_method(http_method="GET", token_required=True, user_required=False,
49
                logger=logger)
48
@api_method(http_method="GET", token_required=True, user_required=False,
49
            logger=logger)
50 50
@user_from_token  # Authenticate user!!
51 51
def get_endpoints(request, token):
52
    if token != api.get_token(request):
53
        raise api.faults.Forbidden()
52
    if token != get_token(request):
53
        raise faults.Forbidden()
54 54

  
55 55
    belongsTo = request.GET.get('belongsTo')
56 56
    if belongsTo and belongsTo != request.user.uuid:
57
        raise api.faults.BadRequest()
57
        raise faults.BadRequest()
58 58

  
59 59
    marker = request.GET.get('marker', 0)
60 60
    limit = request.GET.get('limit', 10000)
61 61

  
62
    endpoints = list(Service.objects.all().order_by('id').\
63
        filter(id__gt=marker)[:limit].\
64
        values('name', 'url', 'api_url', 'id', 'type'))
62
    endpoints = list(Service.objects.all().order_by('id').
63
                     filter(id__gt=marker)[:limit].
64
                     values('name', 'url', 'api_url', 'id', 'type'))
65 65
    for e in endpoints:
66
        e['api_url'] = e['api_url'] or e['url']
67
        e['internalURL'] = e['url']
66
        e['publicURL'] = e['admiURL'] = e['internalURL'] = e['api_url']
67
        e['SNF:uiURL'] = e['url']
68 68
        e['region'] = e['name']
69
        rename_meta_key(e, 'api_url', 'adminURL')
70
        rename_meta_key(e, 'url', 'publicURL')
69
        e.pop('api_url')
71 70

  
72 71
    if endpoints:
73 72
        parts = list(urlsplit(request.path))
......
83 82
        return xml_response(result, 'api/endpoints.xml')
84 83
    else:
85 84
        return json_response(result)
85

  
86

  
87
@csrf_exempt
88
@api_method(http_method="POST", token_required=False, user_required=False,
89
            logger=logger)
90
def authenticate(request):
91
    req = utils.get_request_dict(request)
92

  
93
    uuid = None
94
    try:
95
        token_id = req['auth']['token']['id']
96
    except KeyError:
97
        try:
98
            token_id = req['auth']['passwordCredentials']['password']
99
            uuid = req['auth']['passwordCredentials']['username']
100
        except KeyError:
101
            raise faults.BadRequest('Malformed request')
102

  
103
    if token_id is None:
104
        raise faults.BadRequest('Malformed request')
105

  
106
    try:
107
        user = AstakosUser.objects.get(auth_token=token_id)
108
    except AstakosUser.DoesNotExist:
109
        raise faults.Unauthorized('Invalid token')
110

  
111
    if uuid is not None:
112
        if user.uuid != uuid:
113
            raise faults.Unauthorized('Invalid credentials')
114

  
115
    access = {}
116
    access['token'] = {'id': user.auth_token,
117
                       'expires': utils.isoformat(user.auth_token_expires),
118
                       'tenant': {'id': user.uuid, 'name': user.realname}}
119
    access['user'] = {'id': user.uuid, 'name': user.realname,
120
                      'roles': list(user.groups.values('id', 'name')),
121
                      'roles_links': []}
122
    access['serviceCatalog'] = []
123
    append = access['serviceCatalog'].append
124
    for s in Service.objects.all().order_by('id'):
125
        append({'name': s.name, 'type': s.type,
126
                'endpoints': [{'adminURL': s.api_url,
127
                               'publicURL': s.api_url,
128
                               'internalUrl': s.api_url,
129
                               'SNF:uiURL': s.url,
130
                               'region': s.name}]})
131

  
132
    if request.serialization == 'xml':
133
        return xml_response(access, 'api/access.xml')
134
    else:
135
        return json_response(access)
b/snf-astakos-app/astakos/api/urls.py
59 59

  
60 60
urlpatterns += patterns(
61 61
    'astakos.api.tokens',
62
    url(r'tokens/?$', 'authenticate'),
62 63
    url(r'tokens/(?P<token>.+?)/endpoints', 'get_endpoints'),
63 64
)
b/snf-astakos-app/astakos/im/tests/api.py
32 32
# or implied, of GRNET S.A.
33 33

  
34 34
from astakos.im.tests.common import *
35
from astakos.im.activation_backends import get_backend
36 35

  
37 36
from django.test import TestCase
38 37

  
39 38
from urllib import quote
40 39
from urlparse import urlparse, parse_qs
41
#from xml.dom import minidom
40
from xml.dom import minidom
42 41

  
43 42
import json
44 43

  
......
388 387
        assert self.user2.is_active is True
389 388

  
390 389
        Service(name='service1', url='http://localhost/service1',
391
                api_url='http://localhost/api/service1').save()
390
                api_url='http://localhost/api/service1',
391
                type='service1').save()
392 392
        Service(name='service2', url='http://localhost/service2',
393
                api_url='http://localhost/api/service2').save()
393
                api_url='http://localhost/api/service2',
394
                type='service2').save()
394 395
        Service(name='service3', url='http://localhost/service3',
395
                api_url='http://localhost/api/service3').save()
396
                api_url='http://localhost/api/service3',
397
                type='service3').save()
396 398

  
397
    def test_get_endpoints(self):
399
    def test_authenticate(self):
398 400
        client = Client()
399 401

  
400
        # Check no token
401
        url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token)
402
        r = client.get(url)
402
        # Check not allowed method
403
        url = '/astakos/api/tokens'
404
        r = client.get(url, post_data={})
405
        self.assertEqual(r.status_code, 400)
406

  
407
        # Malformed request
408
        url = '/astakos/api/tokens'
409
        r = client.post(url, post_data={})
410
        self.assertEqual(r.status_code, 400)
411

  
412
        # Check unsupported xml input
413
        url = '/astakos/api/tokens'
414
        post_data = """
415
            <?xml version="1.0" encoding="UTF-8"?>
416
                <auth xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
417
                 xmlns="http://docs.openstack.org/identity/api/v2.0"
418
                 tenantName="%s">
419
                  <passwordCredentials username="%s" password="%s"/>
420
                </auth>""" % (self.user1.uuid, self.user1.uuid,
421
                              self.user1.auth_token)
422
        r = client.post(url, post_data, content_type='application/xml')
423
        self.assertEqual(r.status_code, 400)
424
        body = json.loads(r.content)
425
        self.assertEqual(body['badRequest']['message'],
426
                         "Unsupported Content-type: 'application/xml'")
427

  
428
        # Check malformed request: missing password
429
        url = '/astakos/api/tokens'
430
        post_data = """{"auth":{"passwordCredentials":{"username":"%s"},
431
                                "tenantName":"%s"}}""" % (
432
            self.user1.uuid, self.user1.uuid)
433
        r = client.post(url, post_data, content_type='application/json')
434
        self.assertEqual(r.status_code, 400)
435
        body = json.loads(r.content)
436
        self.assertEqual(body['badRequest']['message'],
437
                         'Malformed request')
438

  
439
        # Check malformed request: missing username
440
        url = '/astakos/api/tokens'
441
        post_data = """{"auth":{"passwordCredentials":{"password":"%s"},
442
                                "tenantName":"%s"}}""" % (
443
            self.user1.auth_token, self.user1.uuid)
444
        r = client.post(url, post_data, content_type='application/json')
445
        self.assertEqual(r.status_code, 400)
446
        body = json.loads(r.content)
447
        self.assertEqual(body['badRequest']['message'],
448
                         'Malformed request')
449

  
450
        # Check invalid pass
451
        url = '/astakos/api/tokens'
452
        post_data = """{"auth":{"passwordCredentials":{"username":"%s",
453
                                                       "password":"%s"},
454
                                "tenantName":"%s"}}""" % (
455
            self.user1.uuid, '', self.user1.uuid)
456
        r = client.post(url, post_data, content_type='application/json')
403 457
        self.assertEqual(r.status_code, 401)
458
        body = json.loads(r.content)
459
        self.assertEqual(body['unauthorized']['message'],
460
                         'Invalid token')
461

  
462
        # Check inconsistent pass
463
        url = '/astakos/api/tokens'
464
        post_data = """{"auth":{"passwordCredentials":{"username":"%s",
465
                                                       "password":"%s"},
466
                                "tenantName":"%s"}}""" % (
467
            self.user1.uuid, self.user2.auth_token, self.user1.uuid)
468
        r = client.post(url, post_data, content_type='application/json')
469
        self.assertEqual(r.status_code, 401)
470
        body = json.loads(r.content)
471
        self.assertEqual(body['unauthorized']['message'],
472
                         'Invalid credentials')
473

  
474
        # Check invalid json data
475
        url = '/astakos/api/tokens'
476
        r = client.post(url, "not json", content_type='application/json')
477
        self.assertEqual(r.status_code, 400)
478
        body = json.loads(r.content)
479
        self.assertEqual(body['badRequest']['message'], 'Invalid JSON data')
480

  
481
        # Check auth with token
482
        url = '/astakos/api/tokens'
483
        post_data = """{"auth":{"token": {"id":"%s"},
484
                        "tenantName":"%s"}}""" % (
485
            self.user1.auth_token, self.user1.uuid)
486
        r = client.post(url, post_data, content_type='application/json')
487
        self.assertEqual(r.status_code, 200)
488
        self.assertTrue(r['Content-Type'].startswith('application/json'))
489
        try:
490
            body = json.loads(r.content)
491
        except Exception, e:
492
            self.fail(e)
493

  
494
        # Check successful json response
495
        url = '/astakos/api/tokens'
496
        post_data = """{"auth":{"passwordCredentials":{"username":"%s",
497
                                                       "password":"%s"},
498
                                "tenantName":"%s"}}""" % (
499
            self.user1.uuid, self.user1.auth_token, self.user1.uuid)
500
        r = client.post(url, post_data, content_type='application/json')
501
        self.assertEqual(r.status_code, 200)
502
        self.assertTrue(r['Content-Type'].startswith('application/json'))
503
        try:
504
            body = json.loads(r.content)
505
        except Exception, e:
506
            self.fail(e)
507

  
508
        try:
509
            token = body['token']['id']
510
            user = body['user']['id']
511
            service_catalog = body['serviceCatalog']
512
        except KeyError:
513
            self.fail('Invalid response')
514

  
515
        self.assertEqual(token, self.user1.auth_token)
516
        self.assertEqual(user, self.user1.uuid)
517
        self.assertEqual(len(service_catalog), 3)
518

  
519
        # Check successful xml response
520
        url = '/astakos/api/tokens'
521
        headers = {'HTTP_ACCEPT': 'application/xml'}
522
        post_data = """{"auth":{"passwordCredentials":{"username":"%s",
523
                                                       "password":"%s"},
524
                                "tenantName":"%s"}}""" % (
525
            self.user1.uuid, self.user1.auth_token, self.user1.uuid)
526
        r = client.post(url, post_data, content_type='application/json',
527
                        **headers)
528
        self.assertEqual(r.status_code, 200)
529
        self.assertTrue(r['Content-Type'].startswith('application/xml'))
530
        try:
531
            body = minidom.parseString(r.content)
532
        except Exception, e:
533
            self.fail(e)
534

  
535
    def test_get_endpoints(self):
536
        client = Client()
404 537

  
405 538
        # Check in active user token
406 539
        inactive_user = AstakosUser.objects.create(email='test3')
......
414 547
        r = client.get(url)
415 548
        self.assertEqual(r.status_code, 401)
416 549

  
417

  
418 550
        # Check forbidden
419 551
        url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token)
420 552
        headers = {'HTTP_X_AUTH_TOKEN': AstakosUser.objects.create(
......
422 554
        r = client.get(url, **headers)
423 555
        self.assertEqual(r.status_code, 401)
424 556

  
425

  
426 557
        # Check bad request method
427 558
        url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token)
428 559
        r = client.post(url)
b/snf-django-lib/snf_django/lib/api/proxy/__init__.py
47 47
#
48 48
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.10
49 49
# Connection and MUST NOT be communicated by proxies over further connections
50
EXCLUDE_HEADERS = ['Host', 'Cookie', 'Connection']
50
EXCLUDE_HEADERS = ['Host', 'Cookie', 'Connection', 'X-Forwarded-Host']
51 51

  
52 52

  
53 53
def proxy(request, target):

Also available in: Unified diff