Revision d29f0371

b/docs/astakos-api-guide.rst
314 314
Fallback call which receives the user token or the user uuid/token pair and
315 315
returns back the token as well as information about the token holder and the
316 316
services he/she can access.
317
If not request body is provided (the request content length is missing or
318
equals to 0) the response contains only non authentication protected
319
information (the service catalog).
317 320

  
318 321
========================================= =========  ==================
319 322
Uri                                       Method     Description
......
361 364
::
362 365

  
363 366
    {"access": {
367
        "token": {
368
            "expires": "2013-06-19T15:23:59.975572+00:00",
369
            "id": "CDEe2k0T/HdiJWBMMbHyOA==",
370
            "tenant": {
371
                "id": "c18088be-16b1-4263-8180-043c54e22903",
372
                "name": "Firstname Lastname"
373
            }
374
        },
364 375
        "serviceCatalog": [
365
           {"SNF:uiURL": "https://node2.example.com/ui/",
366
            "endpoints": [{
367
                "publicURL": "https://object-store.example.synnefo.org/pithos/public/v2.0",
368
                "versionId": "v2.0"}],
369
            "endpoints_links": [],
370
            "name": "pithos_public",
371
            "type": "public"},
372
           {"SNF:uiURL": "https://node2.example.com/ui/",
373
            "endpoints": [{
374
                "publicURL": "https://object-store.example.synnefo.org/pithos/object-store/v1",
375
                "versionId": "v1"}],
376
            "endpoints_links": [],
377
            "name": "pithos_object-store",
378
            "type": "object-store"},
379
           {"SNF:uiURL": "https://node2.example.com/ui/",
380
            "endpoints": [{
381
                "publicURL": "https://object-store.example.synnefo.org/pithos/ui",
382
                "versionId": ""}],
383
            "endpoints_links": [],
384
            "name": "pithos_ui",
385
            "type": "pithos_ui"},
386
           {"SNF:uiURL": "http://localhost:8080",
387
            "endpoints": [{
388
                "publicURL": "https://accounts.example.synnefo.org/ui/v1.0",
389
                "versionId": "v1.0"}],
390
            "endpoints_links": [],
391
            "name": "astakos_ui",
392
            "type": "astakos_ui"},
393
           {"SNF:uiURL": "http://localhost:8080",
394
            "endpoints": [{
395
                "publicURL": "https://accounts.example.synnefo.org/account/v1.0",
396
                "versionId": "v1.0"}],
397
            "endpoints_links": [],
398
            "name": "astakos_account",
399
            "type": "account"},
400
           {"SNF:uiURL": "http://localhost:8080",
401
            "endpoints": [{
402
                "publicURL": "https://accounts.example.synnefo.org/identity/v2.0",
403
                "versionId": "v2.0"}],
404
            "endpoints_links": [],
405
            "name": "astakos_identity",
406
            "type": "identity"}],
407
      "token": {
408
          "expires": "2013-06-19T15:23:59.975572+00:00",
409
           "id": "CDEe2k0T/HdiJWBMMbHyOA",
410
           "tenant": {"id": "c18088be-16b1-4263-8180-043c54e22903",
411
            "name": "Firstname Lastname"}},
412
      "user": {
413
          "id": "c18088be-16b1-4263-8180-043c54e22903",
414
           "name": "Firstname Lastname",
415
           "roles": [{"id": 1, "name": "default"},
416
           "roles_links": []}}}
376
            {"endpoints_links": [],
377
             "endpoints": [{
378
                "SNF:uiURL": "https://accounts.example.synnefo.org/ui",
379
                "versionId": "v1.0",
380
                "publicURL": "https://accounts.example.synnefo.org/account/v1.0"}],
381
             "type": "account",
382
             "name": "astakos_account"},
383
            {"endpoints_links": [],
384
             "endpoints": [{
385
                 "SNF:uiURL": "https://accounts.example.synnefo.org/ui",
386
                 "versionId": "v2.0",
387
                 "publicURL": "https://accounts.example.synnefo.org/account/v2.0"}],
388
             "type": "identity",
389
             "name": "astakos_identity"},
390
            {"endpoints_links": [],
391
             "endpoints": [{
392
                 "SNF:uiURL": "https://cyclades.example.synnefo.org/ui",
393
                 "versionId": "v2.0",
394
                 "publicURL": "https://cyclades.example.synnefo.org/cyclades/compute/v2.0"}],
395
             "type": "compute",
396
             "name": "cyclades_compute"},
397
            {"endpoints_links": [],
398
             "endpoints": [{
399
                 "SNF:uiURL": "https://cyclades.example.synnefo.org/ui",
400
                 "versionId": "v1.0",
401
                 "publicURL": "https://cyclades.example.synnefo.org/cyclades/vmapi/v1.0"}],
402
             "type": "cyclades_vmapi",
403
             "name": "cyclades_vmapi"},
404
            {"endpoints_links": [],
405
             "endpoints": [{
406
                 "SNF:uiURL": "https://cyclades.example.synnefo.org/ui",
407
                 "versionId": "v1.0",
408
                 "publicURL": "https://cyclades.example.synnefo.org/cyclades/image/v1.0"}],
409
             "type": "image",
410
             "name": "cyclades_plankton"},
411
            {"endpoints_links": [],
412
             "endpoints": [{
413
                 "SNF:uiURL": "https://object-store.example.synnefo.org/ui",
414
                 "versionId": "v2.0",
415
                 "publicURL": "https://object-store.example.synnefo.org/pithos/public/v2.0"}],
416
             "type": "public",
417
             "name": "pithos_public"},
418
            {"endpoints_links": [],
419
             "endpoints": [{
420
                 "SNF:uiURL": "https://object-store.example.synnefo.org/ui",
421
                 "versionId": "v1",
422
                 "publicURL": "https://object-store.example.synnefo.org/pithos/object-store/v1"}],
423
             "type": "object-store",
424
             "name": "pithos_object-store"}],
425
         "user": {
426
             "roles_links": [],
427
             "id": "c18088be-16b1-4263-8180-043c54e22903",
428
             "roles": [{"id": 1, "name": "default"}],
429
             "name": "Firstname Lastname"}}}
417 430

  
418 431
Example xml response:
419 432

  
......
423 436

  
424 437
    <access xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
425 438
        xmlns="http://docs.openstack.org/identity/api/v2.0">
426
        <token id="CDEe2k0T/HdiJWBMMbHyOA" expires="2013-06-19T15:23:59.975572+00:00">
439
        <token id="CDEe2k0T/HdiJWBMMbHyOA==" expires="2013-06-19T15:23:59.975572+00:00">
427 440
            <tenant id="c18088be-16b1-4263-8180-043c54e22903" name="Firstname Lastname" />
428 441
        </token>
429 442
        <user id="c18088be-16b1-4263-8180-043c54e22903" name="Firstname Lastname">
......
432 445
            </roles>
433 446
        </user>
434 447
        <serviceCatalog>
435
            <service type="public" name="pithos_public" SNF:uiURL="">
436
                    <endpoint
437
                            versionId="v2.0"
438
                            publicURL="https://object-store.example.synnefo.org/pithos/public/v2.0"
448
            <service type="account" name="astakos_account">
449
                <endpoint  SNF:uiURL="https://accounts.example.synnefo.org/ui"  versionId="v1.0"  publicURL="https://accounts.example.synnefo.org/account/v1.0"  />
439 450
            </service>
440
            <service type="object-store" name="pithos_object-store" SNF:uiURL="">
441
                    <endpoint
442
                            versionId="v1"
443
                            publicURL="https://object-store.example.synnefo.org/pithos/object-store/v1"
451
            <service type="identity" name="astakos_identity">
452
                <endpoint  SNF:uiURL="https://accounts.example.synnefo.org/ui"  versionId="v2.0"  publicURL="https://accounts.example.synnefo.org/account/v2.0"  />
444 453
            </service>
445
            <service type="pithos_ui" name="pithos_ui" SNF:uiURL="">
446
                    <endpoint
447
                            versionId=""
448
                            publicURL="https://object-store.example.synnefo.org/pithos/ui"
454
            <service type="compute" name="cyclades_compute">
455
                <endpoint  SNF:uiURL="https://cyclades.example.synnefo.org/ui"  versionId="v2.0"  publicURL="https://cyclades.example.synnefo.org/cyclades/compute/v2.0"  />
449 456
            </service>
450
            <service type="astakos_ui" name="astakos_ui" SNF:uiURL="">
451
                    <endpoint
452
                            versionId="v1.0"
453
                            publicURL="https://accounts.example.synnefo.org/ui/v1.0"
457
            <service type="cyclades_vmapi" name="cyclades_vmapi">
458
                <endpoint  SNF:uiURL="https://cyclades.example.synnefo.org/ui"  versionId="v1.0"  publicURL="https://cyclades.example.synnefo.org/cyclades/vmapi/v1.0"  />
454 459
            </service>
455
            <service type="account" name="astakos_account" SNF:uiURL="">
456
                    <endpoint
457
                            versionId="v1.0"
458
                            publicURL="https://accounts.example.synnefo.org/account/v1.0"
460
            <service type="image" name="cyclades_plankton">
461
                <endpoint  SNF:uiURL="https://cyclades.example.synnefo.org/ui"  versionId="v1.0"  publicURL="https://cyclades.example.synnefo.org/cyclades/image/v1.0"  />
459 462
            </service>
460
            <service type="identity" name="astakos_identity" SNF:uiURL="">
461
                    <endpoint
462
                            versionId="v2.0"
463
                            publicURL="https://accounts.example.synnefo.org/identity/v2.0"
463
            <service type="public" name="pithos_public">
464
                <endpoint  SNF:uiURL="https://object-store.example.synnefo.org/ui"  versionId="v2.0"  publicURL="https://object-store.example.synnefo.org/pithos/public/v2.0"  />
464 465
            </service>
466
            <service type="object-store" name="pithos_object-store">
467
                <endpoint  SNF:uiURL="https://object-store.example.synnefo.org/ui"  versionId="v1"  publicURL="https://object-store.example.synnefo.org/pithos/object-store/v1"  /> </service>
465 468
        </serviceCatalog>
466 469
    </access>
467 470

  
b/snf-astakos-app/astakos/api/tokens.py
38 38
from snf_django.lib.api import faults, utils, api_method
39 39

  
40 40
from astakos.im.models import Service, AstakosUser
41
from .util import json_response, xml_response, validate_user
41
from .util import json_response, xml_response, validate_user,\
42
    get_content_length
42 43

  
43 44
import logging
44 45
logger = logging.getLogger(__name__)
......
48 49
@api_method(http_method="POST", token_required=False, user_required=False,
49 50
            logger=logger)
50 51
def authenticate(request):
51
    req = utils.get_request_dict(request)
52
    content_length = get_content_length(request)
53
    public_mode = True if not content_length else False
52 54

  
53
    uuid = None
54
    try:
55
        token_id = req['auth']['token']['id']
56
    except KeyError:
55
    d = defaultdict(dict)
56
    if not public_mode:
57
        req = utils.get_request_dict(request)
58

  
59
        uuid = None
57 60
        try:
58
            token_id = req['auth']['passwordCredentials']['password']
59
            uuid = req['auth']['passwordCredentials']['username']
61
            token_id = req['auth']['token']['id']
60 62
        except KeyError:
63
            try:
64
                token_id = req['auth']['passwordCredentials']['password']
65
                uuid = req['auth']['passwordCredentials']['username']
66
            except KeyError:
67
                raise faults.BadRequest('Malformed request')
68

  
69
        if token_id is None:
61 70
            raise faults.BadRequest('Malformed request')
62 71

  
63
    if token_id is None:
64
        raise faults.BadRequest('Malformed request')
72
        try:
73
            user = AstakosUser.objects.get(auth_token=token_id)
74
        except AstakosUser.DoesNotExist:
75
            raise faults.Unauthorized('Invalid token')
65 76

  
66
    try:
67
        user = AstakosUser.objects.get(auth_token=token_id)
68
    except AstakosUser.DoesNotExist:
69
        raise faults.Unauthorized('Invalid token')
77
        validate_user(user)
70 78

  
71
    validate_user(user)
79
        if uuid is not None:
80
            if user.uuid != uuid:
81
                raise faults.Unauthorized('Invalid credentials')
72 82

  
73
    if uuid is not None:
74
        if user.uuid != uuid:
75
            raise faults.Unauthorized('Invalid credentials')
83
        d["access"]["token"] = {
84
            "id": user.auth_token,
85
            "expires": utils.isoformat(user.auth_token_expires),
86
            "tenant": {"id": user.uuid, "name": user.realname}}
87
        d["access"]["user"] = {
88
            "id": user.uuid, 'name': user.realname,
89
            "roles": list(user.groups.values("id", "name")),
90
            "roles_links": []}
76 91

  
77
    d = defaultdict(dict)
78
    d["access"]["token"] = {
79
        "id": user.auth_token,
80
        "expires": utils.isoformat(user.auth_token_expires),
81
        "tenant": {"id": user.uuid, "name": user.realname}}
82
    d["access"]["user"] = {
83
        "id": user.uuid, 'name': user.realname,
84
        "roles": list(user.groups.values("id", "name")),
85
        "roles_links": []}
86 92
    d["access"]["serviceCatalog"] = []
87 93
    append = d["access"]["serviceCatalog"].append
88 94
    for s in Service.objects.all().order_by("id"):
b/snf-astakos-app/astakos/api/util.py
206 206
        return
207 207
    d[new] = d[old]
208 208
    del(d[old])
209

  
210

  
211
def get_int_parameter(p):
212
    if p is not None:
213
        try:
214
            p = int(p)
215
        except ValueError:
216
            return None
217
        if p < 0:
218
            return None
219
    return p
220

  
221

  
222
def get_content_length(request):
223
    content_length = get_int_parameter(request.META.get('CONTENT_LENGTH'))
224
    if content_length is None:
225
        raise faults.LengthRequired('Missing or invalid Content-Length header')
226
    return content_length
b/snf-astakos-app/astakos/im/templates/api/access.xml
2 2
{% load filters %}
3 3
<access xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 4
    xmlns="http://docs.openstack.org/identity/api/v2.0">
5
    {% if d.access.token %}
5 6
    <token id="{{d.access.token.id}}" expires="{{d.access.token.expires}}">
6 7
        <tenant id="{{d.access.token.tenant.id}}" name="{{d.access.token.tenant.name}}" />
7 8
    </token>
9
    {% endif %}
10
    {% if d.access.user %}
8 11
    <user id="{{d.access.user.id}}" name="{{d.access.user.name}}">
9 12
        <roles>
10 13
            {% for r in d.access.user.roles %}
......
12 15
            {% endfor %}
13 16
        </roles>
14 17
    </user>
18
    {% endif %}
15 19
    <serviceCatalog>
16 20
    {% for s in d.access.serviceCatalog %}
17
        <service type="{{s.type}}" name="{{s.name}}" SNF:uiURL="{{s.component.url}}">
21
        <service type="{{s.type}}" name="{{s.name}}">
18 22
            {% for e in s.endpoints %}
19 23
            <endpoint {% for k, v in e.items %} {{k}}="{{v}}" {% endfor %} />
20 24
            {% endfor %}
b/snf-astakos-app/astakos/im/tests/api.py
426 426
        r = client.get(url, post_data={})
427 427
        self.assertEqual(r.status_code, 400)
428 428

  
429
        # Malformed request
430
        url = reverse('astakos.api.tokens.authenticate')
431
        r = client.post(url, post_data={})
432
        self.assertEqual(r.status_code, 400)
429
        # check public mode
430
        r = client.post(url, CONTENT_LENGTH=0)
431
        self.assertEqual(r.status_code, 200)
432
        self.assertTrue(r['Content-Type'].startswith('application/json'))
433
        try:
434
            body = json.loads(r.content)
435
        except Exception, e:
436
            self.fail(e)
437
        self.assertTrue('token' not in body.get('access'))
438
        self.assertTrue('user' not in body.get('access'))
439
        self.assertTrue('serviceCatalog' in body.get('access'))
433 440

  
434 441
        # Check unsupported xml input
435 442
        url = reverse('astakos.api.tokens.authenticate')
......
553 560
#            body = minidom.parseString(r.content)
554 561
#        except Exception, e:
555 562
#            self.fail(e)
563

  
564
        # test public mode: json response

Also available in: Unified diff