Revision 7ac2131c

b/snf-astakos-app/astakos/api/tokens.py
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 urlparse import urlunsplit, urlsplit
35

  
36
from django.http import urlencode
37

  
38
from snf_django.lib import api
39

  
40
from astakos.im.models import Service
41

  
42
from .util import user_from_token, rename_meta_key, json_response, xml_response
43

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

  
47

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

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

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

  
62
    endpoints = list(Service.objects.all().order_by('id').\
63
        filter(id__gt=marker)[:limit].\
64
        values('name', 'url', 'api_url', 'id', 'type'))
65
    for e in endpoints:
66
        e['api_url'] = e['api_url'] or e['url']
67
        e['internalURL'] = e['url']
68
        e['region'] = e['name']
69
        rename_meta_key(e, 'api_url', 'adminURL')
70
        rename_meta_key(e, 'url', 'publicURL')
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)
b/snf-astakos-app/astakos/api/urls.py
56 56
    'astakos.api.service',
57 57
    url(r'^service/user_catalogs/?$', 'get_uuid_displayname_catalogs'),
58 58
)
59

  
60
urlpatterns += patterns(
61
    'astakos.api.tokens',
62
    url(r'tokens/(?P<token>.+?)/endpoints', 'get_endpoints'),
63
)
b/snf-astakos-app/astakos/api/util.py
36 36

  
37 37
from django.http import HttpResponse
38 38
from django.utils import simplejson as json
39
from django.template.loader import render_to_string
39 40

  
40 41
from astakos.im.models import AstakosUser, Service
41 42
from snf_django.lib.api import faults
......
60 61
    return response
61 62

  
62 63

  
64
def xml_response(content, template, status_code=None):
65
    response = HttpResponse()
66
    if status_code is not None:
67
        response.status_code = status_code
68

  
69
    response.content = render_to_string(template, content)
70
    response['Content-Type'] = 'application/xml; charset=UTF-8'
71
    response['Content-Length'] = len(response.content)
72
    return response
73

  
74

  
63 75
def is_integer(x):
64 76
    return isinstance(x, (int, long))
65 77

  
......
174 186
    except:
175 187
        return HttpResponse(status=502)
176 188
    return HttpResponse(status=200)
189

  
190

  
191
def rename_meta_key(d, old, new):
192
    if old not in d:
193
        return
194
    d[new] = d[old]
195
    del(d[old])
b/snf-astakos-app/astakos/im/management/commands/service-add.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
from optparse import make_option
35

  
34 36
from django.core.management.base import BaseCommand, CommandError
37
from django.db.utils import IntegrityError
35 38

  
36 39
from astakos.im.models import Service
37 40

  
......
40 43
    args = "<name> <service URL> <API URL> "
41 44
    help = "Register a service"
42 45

  
46
    option_list = BaseCommand.option_list + (
47
        make_option('--type',
48
                    dest='type',
49
                    help="Service type"),
50
    )
51

  
43 52
    def handle(self, *args, **options):
44 53
        if len(args) < 2:
45 54
            raise CommandError("Invalid number of arguments")
46 55

  
47
        name = args[0]
48
        api_url = args[1]
49
        url = args[2]
50

  
51
        try:
52
            s = Service.objects.get(name=name)
53
            m = "There already exists service named '%s'." % name
54
            raise CommandError(m)
55
        except Service.DoesNotExist:
56
            pass
56
        kwargs = dict(name=args[0], api_url=args[1], url=args[2])
57
        if options['type']:
58
            kwargs['type'] = options['type']
57 59

  
58
        services = list(Service.objects.filter(api_url=api_url))
60
        services = list(Service.objects.filter(api_url=kwargs['api_url']))
59 61
        if services:
60
            m = "URL '%s' is registered for another service." % api_url
62
            m = "URL '%s' is registered for another service." %\
63
                kwargs['api_url']
61 64
            raise CommandError(m)
62 65

  
63 66
        try:
64
            s = Service.objects.create(name=name, api_url=api_url, url=url)
65
        except BaseException as e:
67
            s = Service.objects.create(**kwargs)
68
        except IntegrityError:
69
            m = "There already exists service named '%s'." % kwargs['name']
70
            raise CommandError(m)
71
        except BaseException:
66 72
            raise CommandError("Failed to create service.")
67 73
        else:
68 74
            self.stdout.write('Token: %s\n' % s.auth_token)
b/snf-astakos-app/astakos/im/management/commands/service-list.py
47 47
        "token": ("auth_token", "Authentication token"),
48 48
        "created": ("auth_token_created", "Token creation date"),
49 49
        "expires": ("auth_token_expires", "Token expiration date"),
50
        "type": ("type", "Service type"),
50 51
    }
51 52

  
52 53
    fields = ["id", "name", "url", "api_url", "token", "created", "expires"]
b/snf-astakos-app/astakos/im/management/commands/service-modify.py
64 64
                    dest='renew_token',
65 65
                    default=False,
66 66
                    help="Renew service auth token"),
67
        make_option('--type',
68
                    dest='type',
69
                    default=None,
70
                    help="Modify service type"),
67 71
    )
68 72

  
69 73
    def handle(self, *args, **options):
......
81 85
        url = options.get('url')
82 86
        auth_token = options.get('auth_token')
83 87
        renew_token = options.get('renew_token')
88
        type = options.get('type')
84 89

  
85 90
        if name:
86 91
            service.name = name
......
97 102
        if renew_token and not auth_token:
98 103
            service.renew_token()
99 104

  
105
        if type:
106
            service.type = type
107

  
100 108
        service.save()
101 109

  
102 110
        if renew_token:
b/snf-astakos-app/astakos/im/migrations/0032_auto__add_field_service_type.py
1
# encoding: utf-8
2
import datetime
3
from south.db import db
4
from south.v2 import SchemaMigration
5
from django.db import models
6

  
7
class Migration(SchemaMigration):
8

  
9
    def forwards(self, orm):
10
        
11
        # Adding field 'Service.type'
12
        db.add_column('im_service', 'type', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank='True'), keep_default=False)
13

  
14

  
15
    def backwards(self, orm):
16
        
17
        # Deleting field 'Service.type'
18
        db.delete_column('im_service', 'type')
19

  
20

  
21
    models = {
22
        'auth.group': {
23
            'Meta': {'object_name': 'Group'},
24
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
25
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
26
            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
27
        },
28
        'auth.permission': {
29
            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
30
            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
31
            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
32
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
33
            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
34
        },
35
        'auth.user': {
36
            'Meta': {'object_name': 'User'},
37
            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
38
            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
39
            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
40
            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
41
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
42
            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
43
            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
44
            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
45
            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
46
            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
47
            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
48
            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
49
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
50
        },
51
        'contenttypes.contenttype': {
52
            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
53
            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
54
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
55
            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
56
            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
57
        },
58
        'im.additionalmail': {
59
            'Meta': {'object_name': 'AdditionalMail'},
60
            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
61
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
62
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
63
        },
64
        'im.approvalterms': {
65
            'Meta': {'object_name': 'ApprovalTerms'},
66
            'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
67
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
68
            'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
69
        },
70
        'im.astakosuser': {
71
            'Meta': {'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
72
            'accepted_email': ('django.db.models.fields.EmailField', [], {'default': 'None', 'max_length': '75', 'null': 'True', 'blank': 'True'}),
73
            'accepted_policy': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
74
            'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
75
            'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
76
            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
77
            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
78
            'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
79
            'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
80
            'deactivated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
81
            'deactivated_reason': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
82
            'disturbed_quota': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
83
            'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
84
            'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
85
            'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
86
            'invitations': ('django.db.models.fields.IntegerField', [], {'default': '100'}),
87
            'is_rejected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
88
            'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
89
            'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
90
            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
91
            'moderated_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
92
            'moderated_data': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
93
            'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
94
            'rejected_reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
95
            'updated': ('django.db.models.fields.DateTimeField', [], {}),
96
            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}),
97
            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True', 'null': 'True'}),
98
            'verification_code': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True', 'null': 'True'}),
99
            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
100
        },
101
        'im.astakosuserauthprovider': {
102
            'Meta': {'ordering': "('module', 'created')", 'unique_together': "(('identifier', 'module', 'user'),)", 'object_name': 'AstakosUserAuthProvider'},
103
            'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
104
            'affiliation': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
105
            'auth_backend': ('django.db.models.fields.CharField', [], {'default': "'astakos'", 'max_length': '255'}),
106
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
107
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
108
            'identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
109
            'info_data': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
110
            'module': ('django.db.models.fields.CharField', [], {'default': "'local'", 'max_length': '255'}),
111
            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_providers'", 'to': "orm['im.AstakosUser']"})
112
        },
113
        'im.astakosuserquota': {
114
            'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
115
            'capacity': ('snf_django.lib.db.fields.IntDecimalField', [], {'max_digits': '38', 'decimal_places': '0'}),
116
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
117
            'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
118
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
119
        },
120
        'im.authproviderpolicyprofile': {
121
            'Meta': {'ordering': "['priority']", 'object_name': 'AuthProviderPolicyProfile'},
122
            'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
123
            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'authpolicy_profiles'", 'symmetrical': 'False', 'to': "orm['auth.Group']"}),
124
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
125
            'is_exclusive': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
126
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
127
            'policy_add': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
128
            'policy_automoderate': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
129
            'policy_create': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
130
            'policy_limit': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'}),
131
            'policy_login': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
132
            'policy_remove': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
133
            'policy_required': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
134
            'policy_switch': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
135
            'priority': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
136
            'provider': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
137
            'users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'authpolicy_profiles'", 'symmetrical': 'False', 'to': "orm['im.AstakosUser']"})
138
        },
139
        'im.chain': {
140
            'Meta': {'object_name': 'Chain'},
141
            'chain': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
142
        },
143
        'im.emailchange': {
144
            'Meta': {'object_name': 'EmailChange'},
145
            'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
146
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
147
            'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
148
            'requested_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
149
            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchanges'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
150
        },
151
        'im.invitation': {
152
            'Meta': {'object_name': 'Invitation'},
153
            'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
154
            'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
155
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
156
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
157
            'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
158
            'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
159
            'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
160
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
161
        },
162
        'im.pendingthirdpartyuser': {
163
            'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'PendingThirdPartyUser'},
164
            'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
165
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
166
            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
167
            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
168
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
169
            'info': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
170
            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
171
            'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
172
            'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
173
            'token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
174
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
175
        },
176
        'im.project': {
177
            'Meta': {'object_name': 'Project'},
178
            'application': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'project'", 'unique': 'True', 'to': "orm['im.ProjectApplication']"}),
179
            'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
180
            'deactivation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
181
            'deactivation_reason': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
182
            'id': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'chained_project'", 'unique': 'True', 'primary_key': 'True', 'db_column': "'id'", 'to': "orm['im.Chain']"}),
183
            'last_approval_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
184
            'members': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosUser']", 'through': "orm['im.ProjectMembership']", 'symmetrical': 'False'}),
185
            'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True', 'null': 'True', 'db_index': 'True'}),
186
            'state': ('django.db.models.fields.IntegerField', [], {'default': '1', 'db_index': 'True'})
187
        },
188
        'im.projectapplication': {
189
            'Meta': {'unique_together': "(('chain', 'id'),)", 'object_name': 'ProjectApplication'},
190
            'applicant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects_applied'", 'to': "orm['im.AstakosUser']"}),
191
            'chain': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'chained_apps'", 'db_column': "'chain'", 'to': "orm['im.Chain']"}),
192
            'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
193
            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
194
            'end_date': ('django.db.models.fields.DateTimeField', [], {}),
195
            'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True'}),
196
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
197
            'issue_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
198
            'limit_on_members_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
199
            'member_join_policy': ('django.db.models.fields.IntegerField', [], {}),
200
            'member_leave_policy': ('django.db.models.fields.IntegerField', [], {}),
201
            'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
202
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects_owned'", 'to': "orm['im.AstakosUser']"}),
203
            'precursor_application': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.ProjectApplication']", 'null': 'True', 'blank': 'True'}),
204
            'resource_grants': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.ProjectResourceGrant']", 'blank': 'True'}),
205
            'response': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
206
            'response_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
207
            'start_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
208
            'state': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'})
209
        },
210
        'im.projectmembership': {
211
            'Meta': {'unique_together': "(('person', 'project'),)", 'object_name': 'ProjectMembership'},
212
            'acceptance_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}),
213
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
214
            'leave_request_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
215
            'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"}),
216
            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Project']"}),
217
            'request_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
218
            'state': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'})
219
        },
220
        'im.projectmembershiphistory': {
221
            'Meta': {'object_name': 'ProjectMembershipHistory'},
222
            'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
223
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
224
            'person': ('django.db.models.fields.BigIntegerField', [], {}),
225
            'project': ('django.db.models.fields.BigIntegerField', [], {}),
226
            'reason': ('django.db.models.fields.IntegerField', [], {}),
227
            'serial': ('django.db.models.fields.BigIntegerField', [], {})
228
        },
229
        'im.projectresourcegrant': {
230
            'Meta': {'unique_together': "(('resource', 'project_application'),)", 'object_name': 'ProjectResourceGrant'},
231
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
232
            'member_capacity': ('snf_django.lib.db.fields.IntDecimalField', [], {'default': '0', 'max_digits': '38', 'decimal_places': '0'}),
233
            'project_application': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.ProjectApplication']", 'null': 'True'}),
234
            'project_capacity': ('snf_django.lib.db.fields.IntDecimalField', [], {'null': 'True', 'max_digits': '38', 'decimal_places': '0'}),
235
            'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"})
236
        },
237
        'im.resource': {
238
            'Meta': {'object_name': 'Resource'},
239
            'allow_in_projects': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
240
            'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
241
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
242
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
243
            'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Service']"}),
244
            'unit': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
245
            'uplimit': ('snf_django.lib.db.fields.IntDecimalField', [], {'default': '0', 'max_digits': '38', 'decimal_places': '0'})
246
        },
247
        'im.serial': {
248
            'Meta': {'object_name': 'Serial'},
249
            'serial': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
250
        },
251
        'im.service': {
252
            'Meta': {'object_name': 'Service'},
253
            'api_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
254
            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
255
            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
256
            'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
257
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
258
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
259
            'type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': "'True'"}),
260
            'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
261
        },
262
        'im.sessioncatalog': {
263
            'Meta': {'object_name': 'SessionCatalog'},
264
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
265
            'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
266
            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'to': "orm['im.AstakosUser']"})
267
        },
268
        'im.usersetting': {
269
            'Meta': {'unique_together': "(('user', 'setting'),)", 'object_name': 'UserSetting'},
270
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
271
            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
272
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"}),
273
            'value': ('django.db.models.fields.IntegerField', [], {})
274
        }
275
    }
276

  
277
    complete_apps = ['im']
b/snf-astakos-app/astakos/im/models.py
99 99
    url = models.CharField(_('Service url'), max_length=255, null=True,
100 100
                           help_text=_("URL the service is accessible from"))
101 101
    api_url = models.CharField(_('Service API url'), max_length=255, null=True)
102
    type = models.CharField(_('Type'), max_length=255, null=True, blank='True')
102 103
    auth_token = models.CharField(_('Authentication Token'), max_length=32,
103 104
                                  null=True, blank=True)
104 105
    auth_token_created = models.DateTimeField(_('Token creation date'),
b/snf-astakos-app/astakos/im/templates/api/endpoints.xml
1
<?xml version="1.0" encoding="UTF-8"?>
2
{% load get_type %}
3
<endpoints xmlns="http://docs.openstack.org/identity/api/v2.0">
4
    {% for e in endpoints %}
5
    <endpoint {%for k,v in e.items %}{% if v %}"{{k}}"="{{v}}" {% endif %}{%endfor%}/>
6
    {% endfor %}
7
</endpoints>
8
<endpoint_links>
9
    {% for l in endpoint_links %}
10
        <endpoint_link {%for k,v in l.items %}"{{k}}"="{{v}}" {%endfor%}/>
11
    {% endfor %}
12
</endpoint_links>
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

  
35 36
from django.test import TestCase
36 37

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

  
42
import json
43

  
37 44
ROOT = '/astakos/api/'
38 45
u = lambda url: ROOT + url
39 46

  
......
361 368
        r11 = system_quota[resource11['name']]
362 369
        self.assertEqual(r11['usage'], 102)
363 370
        self.assertEqual(r11['pending'], 101)
371

  
372

  
373
class TokensApiTest(TestCase):
374
    def setUp(self):
375
        self.user1 = AstakosUser.objects.create(email='test1', is_active=True)
376
        self.user2 = AstakosUser.objects.create(email='test2', is_active=True)
377

  
378
        Service(name='service1', url='http://localhost/service1',
379
                api_url='http://localhost/api/service1').save()
380
        Service(name='service2', url='http://localhost/service2',
381
                api_url='http://localhost/api/service2').save()
382
        Service(name='service3', url='http://localhost/service3',
383
                api_url='http://localhost/api/service3').save()
384

  
385
    def test_get_endpoints(self):
386
        client = Client()
387

  
388
        # Check unauthorized request
389
        url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token)
390
        r = client.get(url)
391
        self.assertEqual(r.status_code, 401)
392

  
393
        # Check bad request method
394
        url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token)
395
        r = client.post(url)
396
        self.assertEqual(r.status_code, 400)
397

  
398
        # Check forbidden
399
        url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token)
400
        headers = {'HTTP_X_AUTH_TOKEN': self.user2.auth_token}
401
        r = client.get(url, **headers)
402
        self.assertEqual(r.status_code, 403)
403

  
404
        url = '/astakos/api/tokens/%s/endpoints' % quote(self.user1.auth_token)
405
        headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token}
406
        r = client.get(url, **headers)
407
        self.assertEqual(r.status_code, 200)
408
        self.assertEqual(r['Content-Type'], 'application/json; charset=UTF-8')
409
        try:
410
            body = json.loads(r.content)
411
        except:
412
            self.fail('json format expected')
413
        endpoints = body.get('endpoints')
414
        self.assertEqual(len(endpoints), 3)
415

  
416
        # Check belongsTo BadRequest
417
        url = '/astakos/api/tokens/%s/endpoints?belongsTo=%s' % (
418
            quote(self.user1.auth_token), quote(self.user2.uuid))
419
        headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token}
420
        r = client.get(url, **headers)
421
        self.assertEqual(r.status_code, 400)
422

  
423
         # Check xml serialization
424
        url = '/astakos/api/tokens/%s/endpoints?format=xml' %\
425
            quote(self.user1.auth_token)
426
        headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token}
427
        r = client.get(url, **headers)
428
        self.assertEqual(r.status_code, 200)
429
        self.assertEqual(r['Content-Type'], 'application/xml; charset=UTF-8')
430
#        try:
431
#            body = minidom.parseString(r.content)
432
#        except Exception, e:
433
#            self.fail('xml format expected')
434
        endpoints = body.get('endpoints')
435
        self.assertEqual(len(endpoints), 3)
436

  
437
        # Check limit
438
        url = '/astakos/api/tokens/%s/endpoints?limit=2' %\
439
            quote(self.user1.auth_token)
440
        headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token}
441
        r = client.get(url, **headers)
442
        self.assertEqual(r.status_code, 200)
443
        body = json.loads(r.content)
444
        endpoints = body.get('endpoints')
445
        self.assertEqual(len(endpoints), 2)
446

  
447
        endpoint_link = body.get('endpoint_links', [])[0]
448
        next = endpoint_link.get('href')
449
        p = urlparse(next)
450
        params = parse_qs(p.query)
451
        self.assertTrue('limit' in params)
452
        self.assertTrue('marker' in params)
453
        self.assertEqual(params['marker'][0], '2')
454

  
455
        # Check marker
456
        headers = {'HTTP_X_AUTH_TOKEN': self.user1.auth_token}
457
        r = client.get(next, **headers)
458
        self.assertEqual(r.status_code, 200)
459
        body = json.loads(r.content)
460
        endpoints = body.get('endpoints')
461
        self.assertEqual(len(endpoints), 1)

Also available in: Unified diff