mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-12-23 07:23:46 +00:00
Add token authentication with expiration of tokens
This commit is contained in:
parent
a5715d69b6
commit
6562f32ebf
7 changed files with 66 additions and 8 deletions
25
api/authentication.py
Normal file
25
api/authentication.py
Normal 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)
|
|
@ -29,6 +29,7 @@ Django settings specific to the API.
|
|||
REST_FRAMEWORK = {
|
||||
'URL_FIELD_NAME': 'api_url',
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||
'api.authentication.ExpiringTokenAuthentication',
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
),
|
||||
'DEFAULT_PERMISSION_CLASSES': (
|
||||
|
@ -41,3 +42,9 @@ API_CONTENT_TYPE_APP_LABEL = 'api'
|
|||
API_CONTENT_TYPE_MODEL = 'api'
|
||||
API_PERMISSION_NAME = 'Can use the API'
|
||||
API_PERMISSION_CODENAME = 'use_api'
|
||||
|
||||
# Activate token authentication
|
||||
API_APPS = (
|
||||
'rest_framework.authtoken',
|
||||
)
|
||||
API_TOKEN_DURATION = 86400 # 24 hours
|
||||
|
|
|
@ -42,6 +42,7 @@ router.register(r'whitelists', views.WhitelistViewSet)
|
|||
|
||||
urlpatterns = [
|
||||
url(r'^', include(router.urls)),
|
||||
url(r'^token-auth/', views.ObtainExpiringAuthToken.as_view())
|
||||
# # Services
|
||||
# url(r'^services/$', views.services),
|
||||
# url(
|
||||
|
|
38
api/views.py
38
api/views.py
|
@ -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.
|
||||
"""
|
||||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
import datetime
|
||||
|
||||
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 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 (
|
||||
User,
|
||||
Club,
|
||||
|
@ -118,6 +116,30 @@ class WhitelistViewSet(viewsets.ReadOnlyModelViewSet):
|
|||
queryset = Whitelist.objects.all()
|
||||
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
|
||||
# @login_required
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
WSGIScriptAlias / PATH/re2o/wsgi.py
|
||||
WSGIProcessGroup re2o
|
||||
WSGIDaemonProcess re2o processes=2 threads=16 maximum-requests=1000 display-name=re2o
|
||||
WSGIPassAuthorization On
|
||||
|
||||
SSLCertificateFile /etc/letsencrypt/live/LE_PATH/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/LE_PATH/privkey.pem
|
||||
|
|
|
@ -19,5 +19,6 @@
|
|||
WSGIScriptAlias / PATH/re2o/wsgi.py
|
||||
WSGIProcessGroup re2o
|
||||
WSGIDaemonProcess re2o processes=2 threads=16 maximum-requests=1000 display-name=re2o
|
||||
WSGIPassAuthorization On
|
||||
|
||||
</VirtualHost>
|
||||
|
|
|
@ -177,3 +177,4 @@ GRAPH_MODELS = {
|
|||
# Activate API
|
||||
if 'api' in INSTALLED_APPS:
|
||||
from api.settings import *
|
||||
INSTALLED_APPS += API_APPS
|
||||
|
|
Loading…
Reference in a new issue