Skip to content
This repository was archived by the owner on Mar 15, 2018. It is now read-only.

Commit fbcb29e

Browse files
author
Andy McKay
committed
Add in enough to get started with django rest framework (bug 875540)
1 parent 7018a4e commit fbcb29e

File tree

6 files changed

+120
-0
lines changed

6 files changed

+120
-0
lines changed

mkt/api/authentication.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.contrib.auth.models import AnonymousUser, User
77

88
import commonware.log
9+
from rest_framework.authentication import BaseAuthentication
910
from tastypie import http
1011
from tastypie.authentication import Authentication
1112

@@ -161,3 +162,19 @@ def is_authenticated(self, request, **kwargs):
161162
except Exception, e:
162163
log.info('Bad shared-secret auth data: %s (%s)', auth, e)
163164
return False
165+
166+
167+
class RestOAuthAuthentication(BaseAuthentication, OAuthAuthentication):
168+
"""
169+
OAuthAuthentication suitable for DRF, wraps around tastypie ones.
170+
171+
Since DRF includes OAuthAuthentication libraries, we can probably
172+
remove all this at some point in the future.
173+
"""
174+
175+
def authenticate(self, request):
176+
result = self.is_authenticated(request)
177+
if (isinstance(result, http.HttpUnauthorized)
178+
or not request.user):
179+
return None
180+
return (request.user, None)

mkt/api/authorization.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import commonware.log
22

3+
from rest_framework.permissions import BasePermission
34
from tastypie.authorization import Authorization, ReadOnlyAuthorization
45

56
from access import acl
@@ -83,3 +84,12 @@ def is_authorized(self, request, object=None):
8384
return True
8485
log.info('Permission authorization failed')
8586
return False
87+
88+
89+
class AllowNone(BasePermission):
90+
91+
def has_permission(self, request, view):
92+
return False
93+
94+
def has_object_permission(self, request, view, obj):
95+
return False

mkt/api/paginator.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from rest_framework import serializers
2+
from rest_framework import pagination
3+
from rest_framework.templatetags.rest_framework import replace_query_param
4+
5+
6+
class NextPageField(serializers.Field):
7+
"""Wrapper to remove absolute_uri."""
8+
page_field = 'page'
9+
10+
def to_native(self, value):
11+
if not value.has_next():
12+
return None
13+
page = value.next_page_number()
14+
request = self.context.get('request')
15+
url = request and request.get_full_path() or ''
16+
return replace_query_param(url, self.page_field, page)
17+
18+
19+
class PreviousPageField(serializers.Field):
20+
"""Wrapper to remove absolute_uri."""
21+
page_field = 'page'
22+
23+
def to_native(self, value):
24+
if not value.has_previous():
25+
return None
26+
page = value.previous_page_number()
27+
request = self.context.get('request')
28+
url = request and request.get_full_path() or ''
29+
return replace_query_param(url, self.page_field, page)
30+
31+
32+
class MetaSerializer(serializers.Serializer):
33+
next = NextPageField(source='*')
34+
prev = PreviousPageField(source='*')
35+
page = serializers.Field(source='number')
36+
total_count = serializers.Field(source='paginator.count')
37+
38+
39+
class CustomPaginationSerializer(pagination.BasePaginationSerializer):
40+
meta = MetaSerializer(source='*') # Takes the page object as the source
41+
results_field = 'objects'

mkt/api/tests/test_authentication.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,30 @@ def test_request_has_role(self):
128128
ok_(self.auth.is_authenticated(self.call()))
129129

130130

131+
class TestRestOAuthAuthentication(TestOAuthAuthentication):
132+
133+
def setUp(self):
134+
super(TestRestOAuthAuthentication, self).setUp()
135+
self.auth = authentication.RestOAuthAuthentication()
136+
137+
def test_accepted(self):
138+
eq_(self.auth.authenticate(self.call()), (self.profile.user, None))
139+
ok_(this_thread_is_pinned())
140+
141+
def test_request_token_fake(self):
142+
c = Mock()
143+
c.key = self.access.key
144+
c.secret = 'mom'
145+
ok_(not self.auth.authenticate(self.call(client=OAuthClient(c))))
146+
147+
def test_request_admin(self):
148+
self.add_group_user(self.profile, 'Admins')
149+
ok_(not self.auth.authenticate(self.call()))
150+
151+
def test_request_has_role(self):
152+
self.add_group_user(self.profile, 'App Reviewers')
153+
ok_(self.auth.authenticate(self.call()))
154+
131155

132156
@patch.object(settings, 'SECRET_KEY', 'gubbish')
133157
class TestSharedSecretAuthentication(TestCase):

mkt/settings.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,3 +335,29 @@ def APPCACHE_MEDIA_DEBUG():
335335
# solitude. The matching setting will have to be on in solitude, otherwise
336336
# it will just laugh at your request.
337337
BANGO_FAKE_REFUNDS = False
338+
339+
REST_FRAMEWORK = {
340+
'DEFAULT_MODEL_SERIALIZER_CLASS':
341+
'rest_framework.serializers.HyperlinkedModelSerializer',
342+
'DEFAULT_AUTHENTICATION_CLASSES': (
343+
'mkt.api.authentication.RestOAuthAuthentication',
344+
),
345+
'DEFAULT_RENDERER_CLASSES': (
346+
'rest_framework.renderers.JSONRenderer',
347+
),
348+
'DEFAULT_PARSER_CLASSES': (
349+
'rest_framework.parsers.JSONParser',
350+
),
351+
'DEFAULT_PERMISSION_CLASSES': (
352+
# By default no-one gets anything. You will have to override this
353+
# in each resource to match your needs.
354+
'mkt.api.authorization.AllowNone',
355+
),
356+
'DEFAULT_PAGINATION_SERIALIZER_CLASS':
357+
'mkt.api.paginator.CustomPaginationSerializer',
358+
'DEFAULT_FILTER_BACKENDS': (
359+
'rest_framework.filters.DjangoFilterBackend',
360+
),
361+
'PAGINATE_BY': 20,
362+
'PAGINATE_BY_PARAM': 'limit'
363+
}

requirements/prod.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ django-celery==3.0.17
2222
django-cronjobs==0.2.3
2323
django_csp==1.0.2
2424
django-extensions==0.9
25+
django-filter==0.6
2526
django-memcached-pool==0.4.1
2627
django-multidb-router==0.5
2728
django-mysql-pool==0.2
@@ -32,6 +33,7 @@ django-quieter-formset==0.3
3233
django-raven-metlog==0.2
3334
django-ratelimit==0.1
3435
django-recaptcha-mozilla==0.0.1
36+
djangorestframework==2.3.5
3537
#django-session-csrf==0.6
3638
django-statsd-mozilla==0.3.8.6
3739
django-storages==1.1.4

0 commit comments

Comments
 (0)