Revision ed9223c7

b/snf-astakos-app/astakos/oa2/migrations/0004_auto__chg_field_redirecturl_url__del_unique_redirecturl_url__chg_field.py
1
# -*- coding: 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

  
8
class Migration(SchemaMigration):
9

  
10
    def forwards(self, orm):
11
        # Removing unique constraint on 'RedirectUrl', fields ['url']
12
        db.delete_unique('oa2_redirecturl', ['url'])
13

  
14

  
15
        # Changing field 'RedirectUrl.url'
16
        db.alter_column('oa2_redirecturl', 'url', self.gf('django.db.models.fields.TextField')())
17

  
18
        # Changing field 'AuthorizationCode.redirect_uri'
19
        db.alter_column('oa2_authorizationcode', 'redirect_uri', self.gf('django.db.models.fields.TextField')(null=True))
20

  
21
        # Changing field 'Token.redirect_uri'
22
        db.alter_column('oa2_token', 'redirect_uri', self.gf('django.db.models.fields.TextField')())
23

  
24
    def backwards(self, orm):
25

  
26
        # Changing field 'RedirectUrl.url'
27
        db.alter_column('oa2_redirecturl', 'url', self.gf('django.db.models.fields.URLField')(max_length=200, unique=True))
28
        # Adding unique constraint on 'RedirectUrl', fields ['url']
29
        db.create_unique('oa2_redirecturl', ['url'])
30

  
31

  
32
        # Changing field 'AuthorizationCode.redirect_uri'
33
        db.alter_column('oa2_authorizationcode', 'redirect_uri', self.gf('django.db.models.fields.CharField')(max_length=255, null=True))
34

  
35
        # Changing field 'Token.redirect_uri'
36
        db.alter_column('oa2_token', 'redirect_uri', self.gf('django.db.models.fields.CharField')(max_length=255))
37

  
38
    models = {
39
        'auth.group': {
40
            'Meta': {'object_name': 'Group'},
41
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
42
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
43
            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
44
        },
45
        'auth.permission': {
46
            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
47
            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
48
            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
49
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
50
            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
51
        },
52
        'auth.user': {
53
            'Meta': {'object_name': 'User'},
54
            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
55
            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
56
            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
57
            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
58
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
59
            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
60
            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
61
            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
62
            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
63
            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
64
            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
65
            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
66
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
67
        },
68
        'contenttypes.contenttype': {
69
            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
70
            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
71
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
72
            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
73
            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
74
        },
75
        'im.astakosuser': {
76
            'Meta': {'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
77
            'accepted_email': ('django.db.models.fields.EmailField', [], {'default': 'None', 'max_length': '75', 'null': 'True', 'blank': 'True'}),
78
            'accepted_policy': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
79
            'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
80
            'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
81
            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
82
            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
83
            'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
84
            'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
85
            'deactivated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
86
            'deactivated_reason': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
87
            'disturbed_quota': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
88
            'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
89
            'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
90
            'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
91
            'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
92
            'is_rejected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
93
            'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
94
            'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}),
95
            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
96
            'moderated_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
97
            'moderated_data': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
98
            'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
99
            'rejected_reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
100
            'updated': ('django.db.models.fields.DateTimeField', [], {}),
101
            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}),
102
            'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
103
            'verification_code': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True', 'null': 'True'}),
104
            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
105
        },
106
        'im.astakosuserquota': {
107
            'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
108
            'capacity': ('django.db.models.fields.BigIntegerField', [], {}),
109
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
110
            'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
111
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
112
        },
113
        'im.resource': {
114
            'Meta': {'object_name': 'Resource'},
115
            'api_visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
116
            'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
117
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
118
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
119
            'service_origin': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
120
            'service_type': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
121
            'ui_visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
122
            'unit': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
123
            'uplimit': ('django.db.models.fields.BigIntegerField', [], {'default': '0'})
124
        },
125
        'oa2.authorizationcode': {
126
            'Meta': {'object_name': 'AuthorizationCode'},
127
            'access_token': ('django.db.models.fields.CharField', [], {'default': "'online'", 'max_length': '100'}),
128
            'client': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['oa2.Client']", 'on_delete': 'models.PROTECT'}),
129
            'code': ('django.db.models.fields.TextField', [], {}),
130
            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2013, 12, 20, 0, 0)'}),
131
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
132
            'redirect_uri': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
133
            'scope': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
134
            'state': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
135
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']", 'on_delete': 'models.PROTECT'})
136
        },
137
        'oa2.client': {
138
            'Meta': {'object_name': 'Client'},
139
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
140
            'identifier': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
141
            'is_trusted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
142
            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
143
            'secret': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True'}),
144
            'type': ('django.db.models.fields.CharField', [], {'default': "'confidential'", 'max_length': '100'}),
145
            'url': ('django.db.models.fields.CharField', [], {'max_length': '255'})
146
        },
147
        'oa2.redirecturl': {
148
            'Meta': {'ordering': "('is_default',)", 'unique_together': "(('client', 'url'),)", 'object_name': 'RedirectUrl'},
149
            'client': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['oa2.Client']", 'on_delete': 'models.PROTECT'}),
150
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
151
            'is_default': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
152
            'url': ('django.db.models.fields.TextField', [], {})
153
        },
154
        'oa2.token': {
155
            'Meta': {'object_name': 'Token'},
156
            'access_token': ('django.db.models.fields.CharField', [], {'default': "'online'", 'max_length': '100'}),
157
            'client': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['oa2.Client']", 'on_delete': 'models.PROTECT'}),
158
            'code': ('django.db.models.fields.TextField', [], {}),
159
            'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2013, 12, 20, 0, 0)'}),
160
            'expires_at': ('django.db.models.fields.DateTimeField', [], {}),
161
            'grant_type': ('django.db.models.fields.CharField', [], {'default': "'authorization_code'", 'max_length': '100'}),
162
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
163
            'redirect_uri': ('django.db.models.fields.TextField', [], {}),
164
            'scope': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
165
            'state': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
166
            'token_type': ('django.db.models.fields.CharField', [], {'default': "'Bearer'", 'max_length': '100'}),
167
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']", 'on_delete': 'models.PROTECT'})
168
        }
169
    }
170

  
171
    complete_apps = ['oa2']
b/snf-astakos-app/astakos/oa2/models.py
58 58
class RedirectUrl(models.Model):
59 59
    client = models.ForeignKey('oa2.Client', on_delete=models.PROTECT)
60 60
    is_default = models.BooleanField(default=True)
61
    url = models.URLField(unique=True)
61
    url = models.TextField()
62 62

  
63 63
    class Meta:
64 64
        ordering = ('is_default', )
......
100 100
class AuthorizationCode(models.Model):
101 101
    user = models.ForeignKey('im.AstakosUser', on_delete=models.PROTECT)
102 102
    code = models.TextField()
103
    redirect_uri = models.CharField(max_length=255, null=True, default=None)
103
    redirect_uri = models.TextField(null=True, default=None)
104 104
    client = models.ForeignKey('oa2.Client', on_delete=models.PROTECT)
105 105
    scope = models.TextField(null=True, default=None)
106 106
    created_at = models.DateTimeField(default=datetime.datetime.now())
......
138 138

  
139 139
    # authorization fields
140 140
    user = models.ForeignKey('im.AstakosUser', on_delete=models.PROTECT)
141
    redirect_uri = models.CharField(max_length=255)
141
    redirect_uri = models.TextField()
142 142
    client = models.ForeignKey('oa2.Client', on_delete=models.PROTECT)
143 143
    scope = models.TextField(null=True, default=None)
144 144
    access_token = models.CharField(max_length=100, choices=ACCESS_TOKEN_TYPES,
b/snf-astakos-app/astakos/oa2/tests/djangobackend.py
377 377
        self.assertEqual(code4.state, 'csrfstate')
378 378
        self.assertEqual(code4.redirect_uri, self.client3_redirect_uri)
379 379

  
380
        params['redirect_uri'] = '%s/more' % self.client3_redirect_uri
380
        # redirect uri startswith the client's registered redirect url
381
        params['redirect_uri'] = '%smore' % self.client3_redirect_uri
382
        self.client.set_credentials('client3', 'secret')
383
        r = self.client.authorize_code('client3', urlparams=params)
384
        self.assertEqual(r.status_code, 400)
385

  
386
        # redirect uri descendant
387
        redirect_uri = '%s/more' % self.client3_redirect_uri
388
        params['redirect_uri'] = redirect_uri
381 389
        self.client.set_credentials('client3', 'secret')
382 390
        r = self.client.authorize_code('client3', urlparams=params)
383 391
        self.assertEqual(r.status_code, 302)
......
389 397
        self.assertParamEqual(redirect5, "state", 'csrfstate')
390 398
        self.assertNoParam(redirect5, "extra_param")
391 399
        self.assertHost(redirect5, "server3.com")
392
        self.assertPath(redirect5, "/handle_code/more")
400
        self.assertPath(redirect5, urlparse.urlparse(redirect_uri).path)
393 401

  
394
        code4 = AuthorizationCode.objects.get(code=redirect5.params['code'][0])
395
        self.assertEqual(code4.state, 'csrfstate')
396
        self.assertEqual(code4.redirect_uri,
402
        code5 = AuthorizationCode.objects.get(code=redirect5.params['code'][0])
403
        self.assertEqual(code5.state, 'csrfstate')
404
        self.assertEqual(code5.redirect_uri,
397 405
                         '%s/more' % self.client3_redirect_uri)
398 406

  
407
        # too long redirect uri
408
        redirect_uri = '%s/%s' % (self.client3_redirect_uri, 'a'*2000)
409
        params['redirect_uri'] = redirect_uri
410
        self.client.set_credentials('client3', 'secret')
411
        r = self.client.authorize_code('client3', urlparams=params)
412
        self.assertEqual(r.status_code, 302)
413
        self.assertCount(AuthorizationCode, 6)
414

  
415
        # redirect is valid
416
        redirect6 = self.get_redirect_url(r)
417
        self.assertParam(redirect6, "code")
418
        self.assertParamEqual(redirect6, "state", 'csrfstate')
419
        self.assertNoParam(redirect6, "extra_param")
420
        self.assertHost(redirect6, "server3.com")
421
        self.assertPath(redirect6, urlparse.urlparse(redirect_uri).path)
422

  
423
        code6 = AuthorizationCode.objects.get(code=redirect6.params['code'][0])
424
        self.assertEqual(code6.state, 'csrfstate')
425
        self.assertEqual(code6.redirect_uri, redirect_uri)
426

  
399 427
    def test_get_token(self):
400 428
        # invalid method
401 429
        r = self.client.get(self.client.token_url)
......
483 511
                    'scope': self.client3_redirect_uri,
484 512
                    'state': None}
485 513
        self.assert_access_token_response(r, expected)
514

  
515
        # generate authorization code with too long redirect_uri
516
        redirect_uri = '%s/%s' % (self.client3_redirect_uri, 'a'*2000)
517
        params = {'redirect_uri': redirect_uri}
518
        r = self.client.authorize_code('client3', urlparams=params)
519
        self.assertCount(AuthorizationCode, 1)
520
        redirect = self.get_redirect_url(r)
521
        code_instance = AuthorizationCode.objects.get(
522
            code=redirect.params['code'][0])
523

  
524
        # valid request
525
        self.client.set_credentials('client3', 'secret')
526
        r = self.client.access_token(code_instance.code,
527
                                     redirect_uri=redirect_uri)
528
        self.assertCount(AuthorizationCode, 0)  # assert code is consumed
529
        self.assertCount(Token, 2)
530
        expected = {'redirect_uri': redirect_uri,
531
                    'scope': redirect_uri,
532
                    'state': None}
533
        self.assert_access_token_response(r, expected)

Also available in: Unified diff