Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / api / tokens.py @ 03ce7d7d

History | View | Annotate | Download (6.2 kB)

1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from collections import defaultdict
35

    
36
from django.views.decorators.csrf import csrf_exempt
37

    
38
from snf_django.lib.api import faults, utils, api_method
39
from django.core.cache import cache
40

    
41
from astakos.im import settings
42
from astakos.im.models import Service, AstakosUser
43
from astakos.oa2.backends.base import OA2Error
44
from astakos.oa2.backends.djangobackend import DjangoBackend
45
from .util import json_response, xml_response, validate_user,\
46
    get_content_length
47

    
48
import logging
49
logger = logging.getLogger(__name__)
50

    
51

    
52
def compute_endpoints():
53
    l = []
54
    for s in Service.objects.all().order_by("id").\
55
            prefetch_related('endpoints__data').\
56
            select_related('component'):
57
        endpoints = []
58
        for e in s.endpoints.all():
59
            endpoint = dict((ed.key, ed.value) for ed in e.data.all())
60
            endpoint["SNF:uiURL"] = s.component.url
61
            endpoint["region"] = "default"
62
            if s.name == 'astakos_weblogin':
63
                endpoint["SNF:webloginURL"] = endpoint["publicURL"]
64
            endpoints.append(endpoint)
65
        l.append({"name": s.name,
66
                  "type": s.type,
67
                  "endpoints": endpoints,
68
                  "endpoints_links": []})
69
    return l
70

    
71

    
72
def get_endpoints():
73
    key = "endpoints"
74
    result = cache.get(key)
75
    if result is None:
76
        result = compute_endpoints()
77
        cache.set(key, result, settings.ENDPOINT_CACHE_TIMEOUT)
78
    return result
79

    
80

    
81
@csrf_exempt
82
@api_method(http_method="POST", token_required=False, user_required=False,
83
            logger=logger)
84
def authenticate(request):
85
    try:
86
        content_length = get_content_length(request)
87
    except faults.LengthRequired:
88
        content_length = None
89

    
90
    public_mode = True if not content_length else False
91

    
92
    d = defaultdict(dict)
93
    if not public_mode:
94
        req = utils.get_request_dict(request)
95

    
96
        uuid = None
97
        try:
98
            token_id = req['auth']['token']['id']
99
        except KeyError:
100
            try:
101
                token_id = req['auth']['passwordCredentials']['password']
102
                uuid = req['auth']['passwordCredentials']['username']
103
            except KeyError:
104
                raise faults.BadRequest(
105
                    'Malformed request: missing credentials')
106

    
107
        tenant = req['auth'].get('tenantName')
108

    
109
        if token_id is None:
110
            raise faults.BadRequest('Malformed request: missing token')
111

    
112
        try:
113
            user = AstakosUser.objects.get(auth_token=token_id)
114
        except AstakosUser.DoesNotExist:
115
            raise faults.Unauthorized('Invalid token')
116

    
117
        validate_user(user)
118

    
119
        if uuid is not None:
120
            if user.uuid != uuid:
121
                raise faults.Unauthorized('Invalid credentials')
122

    
123
        if tenant:
124
            if user.uuid != tenant:
125
                raise faults.BadRequest('Not conforming tenantName')
126

    
127
        d["access"]["token"] = {
128
            "id": user.auth_token,
129
            "expires": utils.isoformat(user.auth_token_expires),
130
            "tenant": {"id": user.uuid, "name": user.realname}}
131
        d["access"]["user"] = {
132
            "id": user.uuid, 'name': user.realname,
133
            "roles": [dict(id=str(g['id']), name=g['name']) for g in
134
                      user.groups.values('id', 'name')],
135
            "roles_links": []}
136

    
137
    d["access"]["serviceCatalog"] = get_endpoints()
138

    
139
    if request.serialization == 'xml':
140
        return xml_response({'d': d}, 'api/access.xml')
141
    else:
142
        return json_response(d)
143

    
144

    
145
@api_method(http_method="GET", token_required=False, user_required=False,
146
            logger=logger)
147
def validate_token(request, token_id):
148
    oa2_backend = DjangoBackend()
149
    try:
150
        token = oa2_backend.consume_token(token_id)
151
    except OA2Error, e:
152
        raise faults.ItemNotFound(e.message)
153

    
154
    belongsTo = request.GET.get('belongsTo')
155
    if belongsTo is not None:
156
        if not belongsTo.startswith(token.scope):
157
            raise faults.ItemNotFound(
158
                "The specified tenant is outside the token's scope")
159

    
160
    d = defaultdict(dict)
161
    d["access"]["token"] = {"id": token.code,
162
                            "expires": token.expires_at,
163
                            "tenant": {"id": token.user.uuid,
164
                                       "name": token.user.realname}}
165
    d["access"]["user"] = {"id": token.user.uuid,
166
                           'name': token.user.realname,
167
                           "roles": [dict(id=str(g['id']), name=g['name']) for
168
                                     g in token.user.groups.values('id',
169
                                                                   'name')],
170
                           "roles_links": []}
171

    
172
    if request.serialization == 'xml':
173
        return xml_response({'d': d}, 'api/access.xml')
174
    else:
175
        return json_response(d)