Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / tests / api.py @ a44a3278

History | View | Annotate | Download (24.1 kB)

1
# Copyright 2011, 2012, 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 astakos.im.tests.common import *
35
from astakos.im.settings import astakos_services, BASE_HOST
36
from synnefo.lib.services import get_service_path
37
from synnefo.lib import join_urls
38

    
39
from django.test import TestCase
40
from django.core.urlresolvers import reverse
41

    
42
#from xml.dom import minidom
43

    
44
import json
45

    
46
ROOT = "/%s/%s/%s/" % (
47
    astakos_settings.BASE_PATH, astakos_settings.ACCOUNTS_PREFIX, 'v1.0')
48
u = lambda url: ROOT + url
49

    
50

    
51
class QuotaAPITest(TestCase):
52
    def test_0(self):
53
        client = Client()
54

    
55
        component1 = Component.objects.create(name="comp1")
56
        register.add_service(component1, "service1", "type1", [])
57
        # custom service resources
58
        resource11 = {"name": "service1.resource11",
59
                      "desc": "resource11 desc",
60
                      "service_type": "type1",
61
                      "service_origin": "service1",
62
                      "allow_in_projects": True}
63
        r, _ = register.add_resource(resource11)
64
        register.update_resource(r, 100)
65
        resource12 = {"name": "service1.resource12",
66
                      "desc": "resource11 desc",
67
                      "service_type": "type1",
68
                      "service_origin": "service1",
69
                      "unit": "bytes"}
70
        r, _ = register.add_resource(resource12)
71
        register.update_resource(r, 1024)
72

    
73
        # create user
74
        user = get_local_user('test@grnet.gr')
75
        quotas.qh_sync_user(user)
76

    
77
        component2 = Component.objects.create(name="comp2")
78
        register.add_service(component2, "service2", "type2", [])
79
        # create another service
80
        resource21 = {"name": "service2.resource21",
81
                      "desc": "resource11 desc",
82
                      "service_type": "type2",
83
                      "service_origin": "service2",
84
                      "allow_in_projects": False}
85
        r, _ = register.add_resource(resource21)
86
        register.update_resource(r, 3)
87

    
88
        resource_names = [r['name'] for r in
89
                          [resource11, resource12, resource21]]
90

    
91
        # get resources
92
        r = client.get(u('resources'))
93
        self.assertEqual(r.status_code, 200)
94
        body = json.loads(r.content)
95
        for name in resource_names:
96
            assertIn(name, body)
97

    
98
        # get quota
99
        r = client.get(u('quotas'))
100
        self.assertEqual(r.status_code, 401)
101

    
102
        headers = {'HTTP_X_AUTH_TOKEN': user.auth_token}
103
        r = client.get(u('quotas/'), **headers)
104
        self.assertEqual(r.status_code, 200)
105
        body = json.loads(r.content)
106
        system_quota = body['system']
107
        assertIn('system', body)
108
        for name in resource_names:
109
            assertIn(name, system_quota)
110

    
111
        r = client.get(u('service_quotas'))
112
        self.assertEqual(r.status_code, 401)
113

    
114
        s1_headers = {'HTTP_X_AUTH_TOKEN': component1.auth_token}
115
        r = client.get(u('service_quotas'), **s1_headers)
116
        self.assertEqual(r.status_code, 200)
117
        body = json.loads(r.content)
118
        assertIn(user.uuid, body)
119

    
120
        r = client.get(u('commissions'), **s1_headers)
121
        self.assertEqual(r.status_code, 200)
122
        body = json.loads(r.content)
123
        self.assertEqual(body, [])
124

    
125
        # issue some commissions
126
        commission_request = {
127
            "force": False,
128
            "auto_accept": False,
129
            "name": "my commission",
130
            "provisions": [
131
                {
132
                    "holder": user.uuid,
133
                    "source": "system",
134
                    "resource": resource11['name'],
135
                    "quantity": 1
136
                },
137
                {
138
                    "holder": user.uuid,
139
                    "source": "system",
140
                    "resource": resource12['name'],
141
                    "quantity": 30000
142
                }]}
143

    
144
        post_data = json.dumps(commission_request)
145
        r = client.post(u('commissions'), post_data,
146
                        content_type='application/json', **s1_headers)
147
        self.assertEqual(r.status_code, 413)
148

    
149
        commission_request = {
150
            "force": False,
151
            "auto_accept": False,
152
            "name": "my commission",
153
            "provisions": [
154
                {
155
                    "holder": user.uuid,
156
                    "source": "system",
157
                    "resource": resource11['name'],
158
                    "quantity": 1
159
                },
160
                {
161
                    "holder": user.uuid,
162
                    "source": "system",
163
                    "resource": resource12['name'],
164
                    "quantity": 100
165
                }]}
166

    
167
        post_data = json.dumps(commission_request)
168
        r = client.post(u('commissions'), post_data,
169
                        content_type='application/json', **s1_headers)
170
        self.assertEqual(r.status_code, 201)
171
        body = json.loads(r.content)
172
        serial = body['serial']
173
        self.assertEqual(serial, 1)
174

    
175
        post_data = json.dumps(commission_request)
176
        r = client.post(u('commissions'), post_data,
177
                        content_type='application/json', **s1_headers)
178
        self.assertEqual(r.status_code, 201)
179
        body = json.loads(r.content)
180
        self.assertEqual(body['serial'], 2)
181

    
182
        post_data = json.dumps(commission_request)
183
        r = client.post(u('commissions'), post_data,
184
                        content_type='application/json', **s1_headers)
185
        self.assertEqual(r.status_code, 201)
186
        body = json.loads(r.content)
187
        self.assertEqual(body['serial'], 3)
188

    
189
        r = client.get(u('commissions'), **s1_headers)
190
        self.assertEqual(r.status_code, 200)
191
        body = json.loads(r.content)
192
        self.assertEqual(body, [1, 2, 3])
193

    
194
        r = client.get(u('commissions/' + str(serial)), **s1_headers)
195
        self.assertEqual(r.status_code, 200)
196
        body = json.loads(r.content)
197
        self.assertEqual(body['serial'], serial)
198
        assertIn('issue_time', body)
199
        provisions = sorted(body['provisions'], key=lambda p: p['resource'])
200
        self.assertEqual(provisions, commission_request['provisions'])
201
        self.assertEqual(body['name'], commission_request['name'])
202

    
203
        r = client.get(u('service_quotas?user=' + user.uuid), **s1_headers)
204
        self.assertEqual(r.status_code, 200)
205
        body = json.loads(r.content)
206
        user_quota = body[user.uuid]
207
        system_quota = user_quota['system']
208
        r11 = system_quota[resource11['name']]
209
        self.assertEqual(r11['usage'], 3)
210
        self.assertEqual(r11['pending'], 3)
211

    
212
        # resolve pending commissions
213
        resolve_data = {
214
            "accept": [1, 3],
215
            "reject": [2, 3, 4],
216
        }
217
        post_data = json.dumps(resolve_data)
218

    
219
        r = client.post(u('commissions/action'), post_data,
220
                        content_type='application/json', **s1_headers)
221
        self.assertEqual(r.status_code, 200)
222
        body = json.loads(r.content)
223
        self.assertEqual(body['accepted'], [1])
224
        self.assertEqual(body['rejected'], [2])
225
        failed = body['failed']
226
        self.assertEqual(len(failed), 2)
227

    
228
        r = client.get(u('commissions/' + str(serial)), **s1_headers)
229
        self.assertEqual(r.status_code, 404)
230

    
231
        # auto accept
232
        commission_request = {
233
            "auto_accept": True,
234
            "name": "my commission",
235
            "provisions": [
236
                {
237
                    "holder": user.uuid,
238
                    "source": "system",
239
                    "resource": resource11['name'],
240
                    "quantity": 1
241
                },
242
                {
243
                    "holder": user.uuid,
244
                    "source": "system",
245
                    "resource": resource12['name'],
246
                    "quantity": 100
247
                }]}
248

    
249
        post_data = json.dumps(commission_request)
250
        r = client.post(u('commissions'), post_data,
251
                        content_type='application/json', **s1_headers)
252
        self.assertEqual(r.status_code, 201)
253
        body = json.loads(r.content)
254
        serial = body['serial']
255
        self.assertEqual(serial, 4)
256

    
257
        r = client.get(u('commissions/' + str(serial)), **s1_headers)
258
        self.assertEqual(r.status_code, 404)
259

    
260
        # malformed
261
        commission_request = {
262
            "auto_accept": True,
263
            "name": "my commission",
264
            "provisions": [
265
                {
266
                    "holder": user.uuid,
267
                    "source": "system",
268
                    "resource": resource11['name'],
269
                }
270
            ]}
271

    
272
        post_data = json.dumps(commission_request)
273
        r = client.post(u('commissions'), post_data,
274
                        content_type='application/json', **s1_headers)
275
        self.assertEqual(r.status_code, 400)
276

    
277
        commission_request = {
278
            "auto_accept": True,
279
            "name": "my commission",
280
            "provisions": "dummy"}
281

    
282
        post_data = json.dumps(commission_request)
283
        r = client.post(u('commissions'), post_data,
284
                        content_type='application/json', **s1_headers)
285
        self.assertEqual(r.status_code, 400)
286

    
287
        r = client.post(u('commissions'), commission_request,
288
                        content_type='application/json', **s1_headers)
289
        self.assertEqual(r.status_code, 400)
290

    
291
        # no holding
292
        commission_request = {
293
            "auto_accept": True,
294
            "name": "my commission",
295
            "provisions": [
296
                {
297
                    "holder": user.uuid,
298
                    "source": "system",
299
                    "resource": "non existent",
300
                    "quantity": 1
301
                },
302
                {
303
                    "holder": user.uuid,
304
                    "source": "system",
305
                    "resource": resource12['name'],
306
                    "quantity": 100
307
                }]}
308

    
309
        post_data = json.dumps(commission_request)
310
        r = client.post(u('commissions'), post_data,
311
                        content_type='application/json', **s1_headers)
312
        self.assertEqual(r.status_code, 404)
313

    
314
        # release
315
        commission_request = {
316
            "provisions": [
317
                {
318
                    "holder": user.uuid,
319
                    "source": "system",
320
                    "resource": resource11['name'],
321
                    "quantity": -1
322
                }
323
            ]}
324

    
325
        post_data = json.dumps(commission_request)
326
        r = client.post(u('commissions'), post_data,
327
                        content_type='application/json', **s1_headers)
328
        self.assertEqual(r.status_code, 201)
329
        body = json.loads(r.content)
330
        serial = body['serial']
331

    
332
        accept_data = {'accept': ""}
333
        post_data = json.dumps(accept_data)
334
        r = client.post(u('commissions/' + str(serial) + '/action'), post_data,
335
                        content_type='application/json', **s1_headers)
336
        self.assertEqual(r.status_code, 200)
337

    
338
        reject_data = {'reject': ""}
339
        post_data = json.dumps(accept_data)
340
        r = client.post(u('commissions/' + str(serial) + '/action'), post_data,
341
                        content_type='application/json', **s1_headers)
342
        self.assertEqual(r.status_code, 404)
343

    
344
        # force
345
        commission_request = {
346
            "force": True,
347
            "provisions": [
348
                {
349
                    "holder": user.uuid,
350
                    "source": "system",
351
                    "resource": resource11['name'],
352
                    "quantity": 100
353
                }]}
354

    
355
        post_data = json.dumps(commission_request)
356
        r = client.post(u('commissions'), post_data,
357
                        content_type='application/json', **s1_headers)
358
        self.assertEqual(r.status_code, 201)
359

    
360
        commission_request = {
361
            "force": True,
362
            "provisions": [
363
                {
364
                    "holder": user.uuid,
365
                    "source": "system",
366
                    "resource": resource11['name'],
367
                    "quantity": -200
368
                }]}
369

    
370
        post_data = json.dumps(commission_request)
371
        r = client.post(u('commissions'), post_data,
372
                        content_type='application/json', **s1_headers)
373
        self.assertEqual(r.status_code, 413)
374

    
375
        r = client.get(u('quotas'), **headers)
376
        self.assertEqual(r.status_code, 200)
377
        body = json.loads(r.content)
378
        system_quota = body['system']
379
        r11 = system_quota[resource11['name']]
380
        self.assertEqual(r11['usage'], 102)
381
        self.assertEqual(r11['pending'], 101)
382

    
383
        # Bad Request
384
        r = client.head(u('commissions'))
385
        self.assertEqual(r.status_code, 400)
386

    
387

    
388
class TokensApiTest(TestCase):
389
    def setUp(self):
390
        backend = activation_backends.get_backend()
391

    
392
        self.user1 = AstakosUser.objects.create(
393
            email='test1', email_verified=True, moderated=True,
394
            is_rejected=False)
395
        backend.activate_user(self.user1)
396
        assert self.user1.is_active is True
397

    
398
        self.user2 = AstakosUser.objects.create(
399
            email='test2', email_verified=True, moderated=True,
400
            is_rejected=False)
401
        backend.activate_user(self.user2)
402
        assert self.user2.is_active is True
403

    
404
        c1 = Component(name='component1', url='http://localhost/component1')
405
        c1.save()
406
        s1 = Service(component=c1, type='type1', name='service1')
407
        s1.save()
408
        e1 = Endpoint(service=s1)
409
        e1.save()
410
        e1.data.create(key='versionId', value='v1.0')
411
        e1.data.create(key='publicURL', value='http://localhost:8000/s1/v1.0')
412

    
413
        s2 = Service(component=c1, type='type2', name='service2')
414
        s2.save()
415
        e2 = Endpoint(service=s2)
416
        e2.save()
417
        e2.data.create(key='versionId', value='v1.0')
418
        e2.data.create(key='publicURL', value='http://localhost:8000/s2/v1.0')
419

    
420
        c2 = Component(name='component2', url='http://localhost/component2')
421
        c2.save()
422
        s3 = Service(component=c2, type='type3', name='service3')
423
        s3.save()
424
        e3 = Endpoint(service=s3)
425
        e3.save()
426
        e3.data.create(key='versionId', value='v2.0')
427
        e3.data.create(key='publicURL', value='http://localhost:8000/s3/v2.0')
428

    
429
    def test_authenticate(self):
430
        client = Client()
431

    
432
        # Check not allowed method
433
        url = reverse('astakos.api.tokens.authenticate')
434
        r = client.get(url, post_data={})
435
        self.assertEqual(r.status_code, 400)
436

    
437
        # check public mode
438
        r = client.post(url, CONTENT_LENGTH=0)
439
        self.assertEqual(r.status_code, 200)
440
        self.assertTrue(r['Content-Type'].startswith('application/json'))
441
        try:
442
            body = json.loads(r.content)
443
        except Exception, e:
444
            self.fail(e)
445
        self.assertTrue('token' not in body.get('access'))
446
        self.assertTrue('user' not in body.get('access'))
447
        self.assertTrue('serviceCatalog' in body.get('access'))
448

    
449
        # Check unsupported xml input
450
        url = reverse('astakos.api.tokens.authenticate')
451
        post_data = """
452
            <?xml version="1.0" encoding="UTF-8"?>
453
                <auth xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
454
                 xmlns="http://docs.openstack.org/identity/api/v2.0"
455
                 tenantName="%s">
456
                  <passwordCredentials username="%s" password="%s"/>
457
                </auth>""" % (self.user1.uuid, self.user1.uuid,
458
                              self.user1.auth_token)
459
        r = client.post(url, post_data, content_type='application/xml')
460
        self.assertEqual(r.status_code, 400)
461
        body = json.loads(r.content)
462
        self.assertEqual(body['badRequest']['message'],
463
                         "Unsupported Content-type: 'application/xml'")
464

    
465
        # Check malformed request: missing password
466
        url = reverse('astakos.api.tokens.authenticate')
467
        post_data = """{"auth":{"passwordCredentials":{"username":"%s"},
468
                                "tenantName":"%s"}}""" % (
469
            self.user1.uuid, self.user1.uuid)
470
        r = client.post(url, post_data, content_type='application/json')
471
        self.assertEqual(r.status_code, 400)
472
        body = json.loads(r.content)
473
        self.assertTrue(body['badRequest']['message'].
474
                        startswith('Malformed request'))
475

    
476
        # Check malformed request: missing username
477
        url = reverse('astakos.api.tokens.authenticate')
478
        post_data = """{"auth":{"passwordCredentials":{"password":"%s"},
479
                                "tenantName":"%s"}}""" % (
480
            self.user1.auth_token, self.user1.uuid)
481
        r = client.post(url, post_data, content_type='application/json')
482
        self.assertEqual(r.status_code, 400)
483
        body = json.loads(r.content)
484
        self.assertTrue(body['badRequest']['message'].
485
                        startswith('Malformed request'))
486

    
487
        # Check invalid pass
488
        url = reverse('astakos.api.tokens.authenticate')
489
        post_data = """{"auth":{"passwordCredentials":{"username":"%s",
490
                                                       "password":"%s"},
491
                                "tenantName":"%s"}}""" % (
492
            self.user1.uuid, '', self.user1.uuid)
493
        r = client.post(url, post_data, content_type='application/json')
494
        self.assertEqual(r.status_code, 401)
495
        body = json.loads(r.content)
496
        self.assertEqual(body['unauthorized']['message'],
497
                         'Invalid token')
498

    
499
        # Check inconsistent pass
500
        url = reverse('astakos.api.tokens.authenticate')
501
        post_data = """{"auth":{"passwordCredentials":{"username":"%s",
502
                                                       "password":"%s"},
503
                                "tenantName":"%s"}}""" % (
504
            self.user1.uuid, self.user2.auth_token, self.user2.uuid)
505
        r = client.post(url, post_data, content_type='application/json')
506
        self.assertEqual(r.status_code, 401)
507
        body = json.loads(r.content)
508
        self.assertEqual(body['unauthorized']['message'],
509
                         'Invalid credentials')
510

    
511
        # Check invalid json data
512
        url = reverse('astakos.api.tokens.authenticate')
513
        r = client.post(url, "not json", content_type='application/json')
514
        self.assertEqual(r.status_code, 400)
515
        body = json.loads(r.content)
516
        self.assertEqual(body['badRequest']['message'], 'Invalid JSON data')
517

    
518
        # Check auth with token
519
        url = reverse('astakos.api.tokens.authenticate')
520
        post_data = """{"auth":{"token": {"id":"%s"},
521
                        "tenantName":"%s"}}""" % (
522
            self.user1.auth_token, self.user1.uuid)
523
        r = client.post(url, post_data, content_type='application/json')
524
        self.assertEqual(r.status_code, 200)
525
        self.assertTrue(r['Content-Type'].startswith('application/json'))
526
        try:
527
            body = json.loads(r.content)
528
        except Exception, e:
529
            self.fail(e)
530

    
531
        # Check malformed request: missing token
532
        url = reverse('astakos.api.tokens.authenticate')
533
        post_data = """{"auth":{"auth_token":{"id":"%s"},
534
                                "tenantName":"%s"}}""" % (
535
            self.user1.auth_token, self.user1.uuid)
536
        r = client.post(url, post_data, content_type='application/json')
537
        self.assertEqual(r.status_code, 400)
538
        body = json.loads(r.content)
539
        self.assertTrue(body['badRequest']['message'].
540
                        startswith('Malformed request'))
541

    
542
        # Check bad request: inconsistent tenant
543
        url = reverse('astakos.api.tokens.authenticate')
544
        post_data = """{"auth":{"token":{"id":"%s"},
545
                                "tenantName":"%s"}}""" % (
546
            self.user1.auth_token, self.user2.uuid)
547
        r = client.post(url, post_data, content_type='application/json')
548
        self.assertEqual(r.status_code, 400)
549
        body = json.loads(r.content)
550
        self.assertEqual(body['badRequest']['message'],
551
                         'Not conforming tenantName')
552

    
553
        # Check bad request: inconsistent tenant
554
        url = reverse('astakos.api.tokens.authenticate')
555
        post_data = """{"auth":{"token":{"id":"%s"},
556
                                "tenantName":""}}""" % (
557
            self.user1.auth_token)
558
        r = client.post(url, post_data, content_type='application/json')
559
        self.assertEqual(r.status_code, 200)
560

    
561
        # Check successful json response
562
        url = reverse('astakos.api.tokens.authenticate')
563
        post_data = """{"auth":{"passwordCredentials":{"username":"%s",
564
                                                       "password":"%s"},
565
                                "tenantName":"%s"}}""" % (
566
            self.user1.uuid, self.user1.auth_token, self.user1.uuid)
567
        r = client.post(url, post_data, content_type='application/json')
568
        self.assertEqual(r.status_code, 200)
569
        self.assertTrue(r['Content-Type'].startswith('application/json'))
570
        try:
571
            body = json.loads(r.content)
572
        except Exception, e:
573
            self.fail(e)
574

    
575
        try:
576
            token = body['access']['token']['id']
577
            user = body['access']['user']['id']
578
            service_catalog = body['access']['serviceCatalog']
579
        except KeyError:
580
            self.fail('Invalid response')
581

    
582
        self.assertEqual(token, self.user1.auth_token)
583
        self.assertEqual(user, self.user1.uuid)
584
        self.assertEqual(len(service_catalog), 3)
585

    
586
        # Check successful xml response
587
        url = reverse('astakos.api.tokens.authenticate')
588
        headers = {'HTTP_ACCEPT': 'application/xml'}
589
        post_data = """{"auth":{"passwordCredentials":{"username":"%s",
590
                                                       "password":"%s"},
591
                                "tenantName":"%s"}}""" % (
592
            self.user1.uuid, self.user1.auth_token, self.user1.uuid)
593
        r = client.post(url, post_data, content_type='application/json',
594
                        **headers)
595
        self.assertEqual(r.status_code, 200)
596
        self.assertTrue(r['Content-Type'].startswith('application/xml'))
597
#        try:
598
#            body = minidom.parseString(r.content)
599
#        except Exception, e:
600
#            self.fail(e)
601

    
602

    
603
class WrongPathAPITest(TestCase):
604
    def test_catch_wrong_account_paths(self, *args):
605
        path = get_service_path(astakos_services, 'account', 'v1.0')
606
        path = join_urls(BASE_HOST, path, 'nonexistent')
607
        response = self.client.get(path)
608
        self.assertEqual(response.status_code, 400)
609
        try:
610
            error = json.loads(response.content)
611
        except ValueError:
612
            self.assertTrue(False)
613

    
614
    def test_catch_wrong_identity_paths(self, *args):
615
        path = get_service_path(astakos_services, 'identity', 'v2.0')
616
        path = join_urls(BASE_HOST, path, 'nonexistent')
617
        response = self.client.get(path)
618
        self.assertEqual(response.status_code, 400)
619
        try:
620
            error = json.loads(response.content)
621
        except ValueError:
622
            self.assertTrue(False)