Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,7 @@ pytest-xdist==1.31.0
cssselect==1.1.0

# PostgreSQL database adapter for the Python
psycopg2-binary==2.8.5
psycopg2-binary==2.8.5

# Automated generation of real Swagger/OpenAPI 2.0 schemas from Django REST Framework code.
drf-yasg==1.20.0
4 changes: 2 additions & 2 deletions src/attendee/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated

from core.authentication import TokenAuthentication
from core.authentication import BearerAuthentication
from attendee.models import Attendee


class AttendeeAPIView(views.APIView):
authentication_classes = [TokenAuthentication]
authentication_classes = [BearerAuthentication]
permission_classes = [IsAuthenticated]

model = Attendee
Expand Down
45 changes: 43 additions & 2 deletions src/core/authentication.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,48 @@
from rest_framework.authentication import TokenAuthentication

from rest_framework import exceptions
from rest_framework import HTTP_HEADER_ENCODING, exceptions
from django.utils.translation import gettext_lazy as _
from .models import Token


class TokenAuthentication(TokenAuthentication):
class BearerAuthentication(TokenAuthentication):
keyword = 'Bearer'
model = Token
def get_model(self):
if self.model is not None:
return self.model
from rest_framework.authtoken.models import Token
return Token

def get_authorization_header(self,request):
"""
Return request's 'Authorization:' header, as a bytestring.
Hide some test client ickyness where the header can be unicode.
"""
auth = request.META.get('HTTP_AUTHORIZATION', b'')
if isinstance(auth, str):
# Work around django test client oddness
auth = auth.encode(HTTP_HEADER_ENCODING)
return auth

def authenticate(self, request):
auth = self.get_authorization_header(request).split()

if not auth :
return None

token = auth[0].decode()

return self.authenticate_credentials(token)

def authenticate_credentials(self, key):
model = self.get_model()
try:
token = model.objects.select_related('user').get(key=key)
except model.DoesNotExist:
raise exceptions.AuthenticationFailed(_('Invalid token.'))

if not token.user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))

return (token.user, token)
4 changes: 2 additions & 2 deletions src/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ class Token(models.Model):
"""
key = models.CharField(_("Key"), max_length=40, primary_key=True)
user = BigForeignKey(
to=settings.AUTH_USER_MODEL,
related_name='auth_token',
to=settings.AUTH_USER_MODEL,
related_name='%(app_label)s_auth_token',
verbose_name=_('user'),
on_delete=models.CASCADE,
)
Expand Down
12 changes: 6 additions & 6 deletions src/events/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from django.http import Http404
from django.utils.timezone import make_naive

from core.authentication import TokenAuthentication
from core.authentication import BearerAuthentication
from events.models import (
CustomEvent, Location, ProposedTalkEvent,
ProposedTutorialEvent, SponsoredEvent, Time, KeynoteEvent
Expand Down Expand Up @@ -54,7 +54,7 @@ def get_queryset(self):


class SpeechListAPIView(APIView):
authentication_classes = [TokenAuthentication]
authentication_classes = [BearerAuthentication]
permission_classes = [IsAuthenticated]

def get(self, request, *args, **kwargs):
Expand All @@ -81,7 +81,7 @@ def get(self, request, *args, **kwargs):


class SpeechListByCategoryAPIView(APIView):
authentication_classes = [TokenAuthentication]
authentication_classes = [BearerAuthentication]
permission_classes = [IsAuthenticated]

def get(self, request, *args, **kwargs):
Expand Down Expand Up @@ -110,7 +110,7 @@ class TutorialDetailAPIView(RetrieveAPIView):


class SpeechDetailAPIView(APIView):
authentication_classes = [TokenAuthentication]
authentication_classes = [BearerAuthentication]
permission_classes = [IsAuthenticated]

def get(self, request, *args, **kwargs):
Expand Down Expand Up @@ -263,7 +263,7 @@ def display(self):


class ScheduleAPIView(APIView):
authentication_classes = [TokenAuthentication]
authentication_classes = [BearerAuthentication]
permission_classes = [IsAuthenticated]

event_querysets = [
Expand Down Expand Up @@ -344,7 +344,7 @@ def get(self, request):


class KeynoteEventListAPIView(ListAPIView):
authentication_classes = [TokenAuthentication]
authentication_classes = [BearerAuthentication]
permission_classes = [IsAuthenticated]

queryset = KeynoteEvent.objects.all()
Expand Down
36 changes: 35 additions & 1 deletion src/pycontw2016/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@
'sorl.thumbnail',
'registry',
'corsheaders',
'rest_framework'
'rest_framework',
'rest_framework.authtoken',
'drf_yasg',
)

LOCAL_APPS = (
Expand All @@ -117,6 +119,38 @@
'attendee'
)

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
'core.authentication.BearerAuthentication',
],
'DEFAULT_PERMISSION_CLASSES':{
'rest_framework.permissions.IsAuthenticated'

}

}

SWAGGER_SETTINGS = {
'SHOW_REQUEST_HEADERS': True,
'SECURITY_DEFINITIONS': {
'Bearer': {
'type': 'apiKey',
'name': 'Authorization',
'in': 'header',
},
},
'USE_SESSION_AUTH': True,
'JSON_EDITOR': True,
'SUPPORTED_SUBMIT_METHODS': [
'get',
'post',
'put',
'delete',
'patch'
],
}

INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS

# Enable Postgres-specific things if we are using it.
Expand Down
25 changes: 24 additions & 1 deletion src/pycontw2016/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,28 @@
from django.conf.urls.static import static
from django.contrib import admin
from django.views.i18n import set_language
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi

from core.views import error_page, flat_page, index
from users.views import user_dashboard


schema_view = get_schema_view(
openapi.Info(
title="Snippets API",
default_version='v1',
description="Test description",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="[email protected]"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)


urlpatterns = i18n_patterns(

# Add top-level URL patterns here.
Expand All @@ -35,7 +52,8 @@
url(r'^api/events/', include('events.api.urls', namespace="events")),
url(r'^set-language/$', set_language, name='set_language'),
url(r'^admin/', admin.site.urls),
url(r'^api/attendee/', include('attendee.api.urls'))
url(r'^api/attendee/', include('attendee.api.urls')),
url(r'^api/users/', include('users.api.urls')),
]

# User-uploaded files like profile pics need to be served in development.
Expand All @@ -45,3 +63,8 @@
if settings.DEBUG:
import debug_toolbar
urlpatterns += [url(r'^__debug__/', include(debug_toolbar.urls))]
urlpatterns += [
url(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
url(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
url(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
6 changes: 3 additions & 3 deletions src/sponsors/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated

from core.authentication import TokenAuthentication
from core.authentication import BearerAuthentication
from sponsors.models import Sponsor, OpenRole


class SponsorAPIView(views.APIView):
authentication_classes = [TokenAuthentication]
authentication_classes = [BearerAuthentication]
permission_classes = [IsAuthenticated]

def get(self, request):
Expand Down Expand Up @@ -42,7 +42,7 @@ def get(self, request):


class JobAPIView(views.APIView):
authentication_classes = [TokenAuthentication]
authentication_classes = [BearerAuthentication]
permission_classes = [IsAuthenticated]

def get(self, request):
Expand Down
Empty file added src/users/api/__init__.py
Empty file.
Empty file added src/users/api/serializers.py
Empty file.
8 changes: 8 additions & 0 deletions src/users/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.urls import path
from users.api.views import CustomAuthToken


urlpatterns = [
path("api-token-auth/", CustomAuthToken.as_view()),

]
54 changes: 54 additions & 0 deletions src/users/api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from rest_framework.response import Response
from rest_framework.authtoken.views import ObtainAuthToken

from core.models import Token
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from rest_framework import exceptions
from django.contrib.auth import get_user_model
from users.models import User
from datetime import datetime, timedelta


class CustomAuthToken(ObtainAuthToken):
@swagger_auto_schema(
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=['username','password'],
order=['username', 'password'],
properties={
'username':openapi.Schema(type=openapi.TYPE_STRING),
'password':openapi.Schema(type=openapi.TYPE_STRING)
},
),
operation_description='Get account token'
)

Comment thread
josix marked this conversation as resolved.
Outdated
def post(self, request):
username = request.data['username']
try:
user = get_user_model().objects.get(email=username)
except User.DoesNotExist:
raise exceptions.AuthenticationFailed(('User matching query does not exist'))

tokens = Token.objects.filter(user=user)
if len(tokens)== 0:
Token.objects.create(user=user)

token = Token.objects.get(user=user)
token = str(token)

token_create_time = Token.objects.get(key=token).created
pre_week_day = datetime.now(token_create_time.tzinfo) + timedelta(days=-7)
if token_create_time < pre_week_day:
Token.objects.get(key=token).delete()
Token.objects.create(user=user)

serializer = self.serializer_class(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']

return Response({
'username': user.email,
'token': token
})