8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-22 03:13:12 +00:00

Add token authentication with expiration of tokens

This commit is contained in:
Maël Kervella 2018-04-21 19:48:33 +00:00
parent a5715d69b6
commit 6562f32ebf
7 changed files with 66 additions and 8 deletions

25
api/authentication.py Normal file
View file

@ -0,0 +1,25 @@
import datetime
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from rest_framework.authentication import TokenAuthentication
from rest_framework import exceptions
class ExpiringTokenAuthentication(TokenAuthentication):
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.'))
token_duration = datetime.timedelta(
seconds=settings.API_TOKEN_DURATION
)
utc_now = datetime.datetime.now(datetime.timezone.utc)
if token.created < utc_now - token_duration:
raise exceptions.AuthenticationFailed(_('Token has expired'))
return (token.user, token)

View file

@ -29,6 +29,7 @@ Django settings specific to the API.
REST_FRAMEWORK = { REST_FRAMEWORK = {
'URL_FIELD_NAME': 'api_url', 'URL_FIELD_NAME': 'api_url',
'DEFAULT_AUTHENTICATION_CLASSES': ( 'DEFAULT_AUTHENTICATION_CLASSES': (
'api.authentication.ExpiringTokenAuthentication',
'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.SessionAuthentication',
), ),
'DEFAULT_PERMISSION_CLASSES': ( 'DEFAULT_PERMISSION_CLASSES': (
@ -41,3 +42,9 @@ API_CONTENT_TYPE_APP_LABEL = 'api'
API_CONTENT_TYPE_MODEL = 'api' API_CONTENT_TYPE_MODEL = 'api'
API_PERMISSION_NAME = 'Can use the API' API_PERMISSION_NAME = 'Can use the API'
API_PERMISSION_CODENAME = 'use_api' API_PERMISSION_CODENAME = 'use_api'
# Activate token authentication
API_APPS = (
'rest_framework.authtoken',
)
API_TOKEN_DURATION = 86400 # 24 hours

View file

@ -42,6 +42,7 @@ router.register(r'whitelists', views.WhitelistViewSet)
urlpatterns = [ urlpatterns = [
url(r'^', include(router.urls)), url(r'^', include(router.urls)),
url(r'^token-auth/', views.ObtainExpiringAuthToken.as_view())
# # Services # # Services
# url(r'^services/$', views.services), # url(r'^services/$', views.services),
# url( # url(

View file

@ -24,17 +24,15 @@ The views for the API app. They should all return JSON data and not fallback on
HTML pages such as the login and index pages for a better integration. HTML pages such as the login and index pages for a better integration.
""" """
from django.contrib.auth.decorators import login_required, permission_required import datetime
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status, mixins, generics, viewsets from rest_framework import viewsets, status
from re2o.utils import (
all_has_access,
all_active_assigned_interfaces,
filter_active_interfaces
)
from users.models import ( from users.models import (
User, User,
Club, Club,
@ -118,6 +116,30 @@ class WhitelistViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Whitelist.objects.all() queryset = Whitelist.objects.all()
serializer_class = WhitelistSerializer serializer_class = WhitelistSerializer
# Subclass the standard rest_framework.auth_token.views.ObtainAuthToken
# in order to renew the lease of the token and add expiration time
class ObtainExpiringAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
token_duration = datetime.timedelta(
seconds=settings.API_TOKEN_DURATION
)
utc_now = datetime.datetime.now(datetime.timezone.utc)
if not created and token.created < utc_now - token_duration:
token.delete()
token = Token.objects.create(user=user)
token.created = datetime.datetime.utcnow()
token.save()
return Response({
'token': token.key,
'expiration_date': token.created + token_duration
})
# #
# @csrf_exempt # @csrf_exempt
# @login_required # @login_required

View file

@ -26,6 +26,7 @@
WSGIScriptAlias / PATH/re2o/wsgi.py WSGIScriptAlias / PATH/re2o/wsgi.py
WSGIProcessGroup re2o WSGIProcessGroup re2o
WSGIDaemonProcess re2o processes=2 threads=16 maximum-requests=1000 display-name=re2o WSGIDaemonProcess re2o processes=2 threads=16 maximum-requests=1000 display-name=re2o
WSGIPassAuthorization On
SSLCertificateFile /etc/letsencrypt/live/LE_PATH/fullchain.pem SSLCertificateFile /etc/letsencrypt/live/LE_PATH/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/LE_PATH/privkey.pem SSLCertificateKeyFile /etc/letsencrypt/live/LE_PATH/privkey.pem

View file

@ -19,5 +19,6 @@
WSGIScriptAlias / PATH/re2o/wsgi.py WSGIScriptAlias / PATH/re2o/wsgi.py
WSGIProcessGroup re2o WSGIProcessGroup re2o
WSGIDaemonProcess re2o processes=2 threads=16 maximum-requests=1000 display-name=re2o WSGIDaemonProcess re2o processes=2 threads=16 maximum-requests=1000 display-name=re2o
WSGIPassAuthorization On
</VirtualHost> </VirtualHost>

View file

@ -177,3 +177,4 @@ GRAPH_MODELS = {
# Activate API # Activate API
if 'api' in INSTALLED_APPS: if 'api' in INSTALLED_APPS:
from api.settings import * from api.settings import *
INSTALLED_APPS += API_APPS