diff --git a/InvenTree/InvenTree/middleware.py b/InvenTree/InvenTree/middleware.py index 9abf3643e3..d6d40e85b6 100644 --- a/InvenTree/InvenTree/middleware.py +++ b/InvenTree/InvenTree/middleware.py @@ -20,10 +20,9 @@ class AuthRequiredMiddleware(object): response = self.get_response(request) + # Redirect any unauthorized HTTP requests to the login page if not request.user.is_authenticated: - print(request.path_info) - - if not request.path_info == reverse_lazy('login'): + if not request.path_info == reverse_lazy('login') and not request.path_info.startswith('/api/'): return HttpResponseRedirect(reverse_lazy('login')) # Code to be executed for each request/response after diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index d5ff5749ba..bb50ed04f4 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -65,14 +65,15 @@ INSTALLED_APPS = [ 'order.apps.OrderConfig', # Third part add-ons - 'django_filters', # Extended filter functionality - 'dbbackup', # Database backup / restore - 'rest_framework', # DRF (Django Rest Framework) - 'corsheaders', # Cross-origin Resource Sharing for DRF - 'crispy_forms', # Improved form rendering - 'import_export', # Import / export tables to file - 'django_cleanup', # Automatically delete orphaned MEDIA files - 'qr_code', # Generate QR codes + 'django_filters', # Extended filter functionality + 'dbbackup', # Database backup / restore + 'rest_framework', # DRF (Django Rest Framework) + 'rest_framework.authtoken', # Token authentication for API + 'corsheaders', # Cross-origin Resource Sharing for DRF + 'crispy_forms', # Improved form rendering + 'import_export', # Import / export tables to file + 'django_cleanup', # Automatically delete orphaned MEDIA files + 'qr_code', # Generate QR codes ] LOGGING = { @@ -131,6 +132,11 @@ TEMPLATES = [ REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler', 'DATETIME_FORMAT': '%Y-%m-%d %H:%M', + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.BasicAuthentication', + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.TokenAuthentication', + ) # 'EXCEPTION_HANDLER': 'InvenTree.utils.api_exception_handler', # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # 'PAGE_SIZE': 50, diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index fbb73c1a12..4b1e449c08 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -336,7 +336,7 @@ part_api_urls = [ url(r'^category/', include(cat_api_urls)), url(r'^star/', include(part_star_api_urls)), - url(r'^(?P\d+)/', PartDetail.as_view(), name='api-part-detail'), + url(r'^(?P\d+)/?', PartDetail.as_view(), name='api-part-detail'), url(r'^.*$', PartList.as_view(), name='api-part-list'), ] @@ -344,7 +344,7 @@ part_api_urls = [ bom_api_urls = [ # BOM Item Detail - url('^(?P\d+)/', BomDetail.as_view(), name='api-bom-detail'), + url(r'^(?P\d+)/?', BomDetail.as_view(), name='api-bom-detail'), # Catch-all url(r'^.*$', BomList.as_view(), name='api-bom-list'), diff --git a/InvenTree/users/serializers.py b/InvenTree/users/serializers.py index 01ce8f44b3..13aadb24b1 100644 --- a/InvenTree/users/serializers.py +++ b/InvenTree/users/serializers.py @@ -8,7 +8,8 @@ class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User - fields = ('username', + fields = ('pk', + 'username', 'first_name', 'last_name', 'email',) diff --git a/InvenTree/users/urls.py b/InvenTree/users/urls.py index 046cab9428..6082ef14df 100644 --- a/InvenTree/users/urls.py +++ b/InvenTree/users/urls.py @@ -5,5 +5,7 @@ from . import views user_urls = [ url(r'^(?P[0-9]+)/?$', views.UserDetail.as_view(), name='user-detail'), + url(r'token', views.GetAuthToken.as_view()), + url(r'^$', views.UserList.as_view()), ] diff --git a/InvenTree/users/views.py b/InvenTree/users/views.py index 20fee70caa..e10fe2c615 100644 --- a/InvenTree/users/views.py +++ b/InvenTree/users/views.py @@ -2,8 +2,13 @@ from rest_framework import generics, permissions from django.contrib.auth.models import User from .serializers import UserSerializer +from rest_framework.authtoken.views import ObtainAuthToken +from rest_framework.authtoken.models import Token +from rest_framework.response import Response + class UserDetail(generics.RetrieveAPIView): + """ Detail endpoint for a single user """ queryset = User.objects.all() serializer_class = UserSerializer @@ -11,7 +16,26 @@ class UserDetail(generics.RetrieveAPIView): class UserList(generics.ListAPIView): + """ List endpoint for detail on all users """ queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + + +class GetAuthToken(ObtainAuthToken): + """ Return authentication token for an authenticated user. """ + + def post(self, request, *args, **kwargs): + serializer = self.serializer_class(data=request.data, + context={'request': request}) + serializer.is_valid(raise_exception=True) + user = serializer.validated_data['user'] + token, created = Token.objects.get_or_create(user=user) + + return Response({ + 'token': token.key, + 'pk': user.pk, + 'username': user.username, + 'email': user.email + }) diff --git a/setup.cfg b/setup.cfg index d6e7ca056a..61a1b69c6f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ ignore = # - W293 - blank lines contain whitespace W293, # - E501 - line too long (82 characters) - E501, + E501, E722, # - C901 - function is too complex C901, exclude = .git,__pycache__,*/migrations/*