0002-add-redirects-uris-field-checking-14147.patch
fargo/oauth2/admin.py | ||
---|---|---|
20 | 20 | |
21 | 21 | |
22 | 22 |
class OAuth2ClientAdmin(admin.ModelAdmin): |
23 |
fields = ('client_name', 'client_id', 'client_secret') |
|
24 |
list_display = ['client_name', 'client_id', 'client_secret'] |
|
23 |
fields = ('client_name', 'client_id', 'client_secret', 'redirect_uris')
|
|
24 |
list_display = ['client_name', 'client_id', 'client_secret', 'redirect_uris']
|
|
25 | 25 |
readonly_fields = ['client_id', 'client_secret'] |
26 | 26 | |
27 | ||
27 | 28 |
admin.site.register(OAuth2Client, OAuth2ClientAdmin) |
fargo/oauth2/migrations/0001_initial.py | ||
---|---|---|
29 | 29 |
('client_secret', models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255)), |
30 | 30 |
('client_id', models.CharField(default=fargo.oauth2.models.generate_uuid, max_length=255)), |
31 | 31 |
('client_name', models.CharField(max_length=255)), |
32 |
('redirect_uri', models.TextField(default=b'')), |
|
32 |
('redirect_uris', models.TextField(verbose_name='redirect URIs', validators=[fargo.oauth2.models.validate_https_url])), |
|
33 |
], |
|
34 |
), |
|
35 |
migrations.CreateModel( |
|
36 |
name='OAuth2TempFile', |
|
37 |
fields=[ |
|
38 |
('hash_key', models.CharField(max_length=128, serialize=False, primary_key=True)), |
|
39 |
('filename', models.CharField(max_length=512)), |
|
40 |
('document', models.ForeignKey(to='fargo.Document')), |
|
33 | 41 |
], |
34 | 42 |
), |
35 | 43 |
] |
fargo/oauth2/migrations/0002_oauth2tempfile.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import models, migrations |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('fargo', '0013_document_mime_type'), |
|
11 |
('oauth2', '0001_initial'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.CreateModel( |
|
16 |
name='OAuth2TempFile', |
|
17 |
fields=[ |
|
18 |
('hash_key', models.CharField(max_length=128, serialize=False, primary_key=True)), |
|
19 |
('filename', models.CharField(max_length=512)), |
|
20 |
('document', models.ForeignKey(to='fargo.Document')), |
|
21 |
], |
|
22 |
), |
|
23 |
] |
fargo/oauth2/models.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
import uuid |
18 | 18 | |
19 |
from django.core.validators import URLValidator |
|
20 |
from django.core.exceptions import ValidationError |
|
19 | 21 |
from django.db import models |
22 |
from django.utils.translation import ugettext_lazy as _ |
|
20 | 23 | |
21 | 24 |
from fargo.fargo.models import Document, UserDocument |
22 | 25 | |
... | ... | |
25 | 28 |
return unicode(uuid.uuid4()) |
26 | 29 | |
27 | 30 | |
31 |
def validate_https_url(data): |
|
32 |
errors = [] |
|
33 |
data = data.strip() |
|
34 |
if not data: |
|
35 |
return |
|
36 |
for url in data.split(): |
|
37 |
try: |
|
38 |
URLValidator(schemes=['http', 'https'])(url) |
|
39 |
except ValidationError as e: |
|
40 |
errors.append(e) |
|
41 |
if errors: |
|
42 |
raise ValidationError(errors) |
|
43 | ||
44 | ||
28 | 45 |
class OAuth2Authorize(models.Model): |
29 | 46 |
user_document = models.ForeignKey(UserDocument) |
30 | 47 |
access_token = models.CharField(max_length=255, default=generate_uuid) |
... | ... | |
39 | 56 |
client_secret = models.CharField(max_length=255, default=generate_uuid) |
40 | 57 |
client_id = models.CharField(max_length=255, default=generate_uuid) |
41 | 58 |
client_name = models.CharField(max_length=255) |
42 |
redirect_uri = models.TextField(default='') |
|
59 |
redirect_uris = models.TextField( |
|
60 |
verbose_name=_('redirect URIs'), |
|
61 |
validators=[validate_https_url]) |
|
43 | 62 | |
44 | 63 |
def __repr__(self): |
45 | 64 |
return 'OAuth2Client name: %s with id: %s' % (self.client_name, self.client_id) |
46 | 65 | |
47 | 66 |
def get_redirect_uris(self): |
48 |
return self.redirect_uri.split() |
|
67 |
return self.redirect_uris.split()
|
|
49 | 68 | |
50 |
def add_redirect_uri(self, redirect_uri): |
|
51 |
self.redirect_uri += ' %s' % redirect_uri |
|
52 |
self.save() |
|
69 |
def check_redirect_uri(self, redirect_uri): |
|
70 |
return redirect_uri in self.redirect_uris.strip().split() |
|
53 | 71 | |
54 | 72 | |
55 | 73 |
class OAuth2TempFile(models.Model): |
fargo/oauth2/views.py | ||
---|---|---|
54 | 54 |
uri = self.error_redirect(redirect_uri, 'unsupported_response_type') |
55 | 55 |
return HttpResponseRedirect(uri) |
56 | 56 |
try: |
57 | ||
58 | 57 |
client = OAuth2Client.objects.get(client_id=client_id) |
59 |
client.add_redirect_uri(redirect_uri) |
|
58 |
if not client.check_redirect_uri(redirect_uri): |
|
59 |
uri = self.error_redirect(redirect_uri, 'invalid_redirect_uri') |
|
60 |
return HttpResponseRedirect(uri) |
|
60 | 61 |
except OAuth2Client.DoesNotExist: |
61 | 62 |
uri = self.error_redirect(redirect_uri, 'unauthorized_client') |
62 | 63 |
return HttpResponseRedirect(uri) |
tests/test_oauth2.py | ||
---|---|---|
1 | 1 |
import pytest |
2 |
import os |
|
3 |
import base64 |
|
4 | 2 |
from urllib import quote |
3 |
import urlparse |
|
5 | 4 | |
6 | 5 |
from django.core.files.base import ContentFile |
7 | 6 |
from django.core.urlresolvers import reverse |
... | ... | |
14 | 13 | |
15 | 14 |
pytestmark = pytest.mark.django_db |
16 | 15 | |
16 | ||
17 | 17 |
@pytest.fixture |
18 | 18 |
def oauth2_client(): |
19 |
return OAuth2Client.objects.create(client_name='test_oauth2') |
|
19 |
return OAuth2Client.objects.create( |
|
20 |
client_name='test_oauth2', client_id='client-id', client_secret='client-secret', |
|
21 |
redirect_uris='https://example.net/document https://doc.example.net/ https://example.com') |
|
22 | ||
20 | 23 | |
21 | 24 |
@pytest.fixture |
22 | 25 |
def document(): |
... | ... | |
25 | 28 | |
26 | 29 |
return Document.objects.get_by_file(content) |
27 | 30 | |
31 | ||
28 | 32 |
@pytest.fixture |
29 | 33 |
def user_doc(document, john_doe): |
30 | 34 |
return UserDocument.objects.create(user=john_doe, document=document, filename='Baudelaire.txt') |
31 | 35 | |
36 | ||
37 |
def assert_error_redirect(url, error): |
|
38 |
assert urlparse.urlparse(url).query == 'error=%s' % error |
|
39 | ||
40 | ||
32 | 41 |
def test_get_document_oauth2(app, john_doe, oauth2_client, user_doc): |
33 | 42 |
login(app, user=john_doe) |
34 | 43 |
url = reverse('oauth2-authorize') |
35 | 44 |
params = { |
36 |
'client_id': oauth2_client.client_id, |
|
37 | 45 |
'client_secret': oauth2_client.client_secret, |
38 |
'redirect_uri': 'https://example.com', |
|
39 | 46 |
'response_type': 'code', |
40 | 47 |
'state': 'achipeachope' |
41 | 48 |
} |
42 | ||
43 |
url += '?%s' % urlencode(params) |
|
44 |
resp = app.get(url) |
|
49 |
# test missing redirect_uri |
|
50 |
resp = app.get(url, params={}, status=400) |
|
51 |
assert resp.content == 'missing parameter redirect_uri' |
|
52 |
# test missing client id |
|
53 |
params['redirect_uri'] = 'https://toto.example.com' |
|
54 |
resp = app.get(url, params=params, status=302) |
|
55 |
assert_error_redirect(resp.url, 'invalid_request') |
|
56 |
# test invalid response type |
|
57 |
params['client_id'] = oauth2_client.client_id |
|
58 |
params['response_type'] = 'token' |
|
59 |
resp = app.get(url, params=params, status=302) |
|
60 |
assert_error_redirect(resp.url, 'unsupported_response_type') |
|
61 |
# test invalid redirect uri |
|
62 |
params['response_type'] = 'code' |
|
63 |
resp = app.get(url, params=params, status=302) |
|
64 |
assert_error_redirect(resp.url, 'invalid_redirect_uri') |
|
65 | ||
66 |
params['redirect_uri'] = 'https://example.com' |
|
67 |
resp = app.get(url, params=params) |
|
45 | 68 | |
46 | 69 |
assert resp.status_code == 200 |
47 | 70 |
assert len(resp.forms[0]['document'].options) == 2 |
... | ... | |
123 | 146 | |
124 | 147 |
assert user_document.filename == 'Baudelaire.txt' |
125 | 148 | |
149 | ||
126 | 150 |
def test_confirm_put_document_file_exception(app, john_doe, user_doc): |
127 | 151 |
login(app, user=john_doe) |
128 | 152 |
oauth_tmp_file = OAuth2TempFile.objects.create(document=user_doc.document, filename=user_doc.filename) |
129 |
- |