From 495312b060c8c99aa7e2d236d0e8e036c650b8ae Mon Sep 17 00:00:00 2001 From: Omma Date: Wed, 21 Aug 2024 16:38:37 +0330 Subject: [PATCH] Core: add jwt auth --- WellService/settings.py | 61 ++++++++++++++++++++++- WellService/urls.py | 9 +++- jwtauth/__init__.py | 0 jwtauth/admin.py | 3 ++ jwtauth/apps.py | 6 +++ jwtauth/migrations/0001_initial.py | 22 +++++++++ jwtauth/migrations/__init__.py | 0 jwtauth/models.py | 7 +++ jwtauth/tests.py | 3 ++ jwtauth/urls.py | 20 ++++++++ jwtauth/views.py | 43 +++++++++++++++++ proxy/views.py | 77 ++++++++++++++++++++++++------ 12 files changed, 234 insertions(+), 17 deletions(-) create mode 100644 jwtauth/__init__.py create mode 100644 jwtauth/admin.py create mode 100644 jwtauth/apps.py create mode 100644 jwtauth/migrations/0001_initial.py create mode 100644 jwtauth/migrations/__init__.py create mode 100644 jwtauth/models.py create mode 100644 jwtauth/tests.py create mode 100644 jwtauth/urls.py create mode 100644 jwtauth/views.py diff --git a/WellService/settings.py b/WellService/settings.py index 0e5f180..e7c2b88 100644 --- a/WellService/settings.py +++ b/WellService/settings.py @@ -39,8 +39,10 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', 'rest_framework', - + 'rest_framework_simplejwt', + 'proxy', + 'jwtauth', ] MIDDLEWARE = [ @@ -125,3 +127,60 @@ STATIC_URL = 'static/' # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + +#JWT + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ) +} + +from datetime import timedelta +... +# Configure Simple JWT +SIMPLE_JWT = { + "AUTH_HEADER_TYPES": ("Bearer",), +} +''' +SIMPLE_JWT = { + "ACCESS_TOKEN_LIFETIME": timedelta(minutes=5), + "REFRESH_TOKEN_LIFETIME": timedelta(days=1), + "ROTATE_REFRESH_TOKENS": False, + "BLACKLIST_AFTER_ROTATION": False, + "UPDATE_LAST_LOGIN": False, + + "ALGORITHM": "HS256", + #"SIGNING_KEY": settings.SECRET_KEY, + "VERIFYING_KEY": "", + "AUDIENCE": None, + "ISSUER": None, + "JSON_ENCODER": None, + "JWK_URL": None, + "LEEWAY": 0, + # This is nessecary! + "AUTH_HEADER_TYPES": ("Bearer",), + "AUTH_HEADER_NAME": "HTTP_AUTHORIZATION", + "USER_ID_FIELD": "id", + "USER_ID_CLAIM": "user_id", + "USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule", + + "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",), + "TOKEN_TYPE_CLAIM": "token_type", + "TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser", + + "JTI_CLAIM": "jti", + + "SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp", + "SLIDING_TOKEN_LIFETIME": timedelta(minutes=5), + "SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1), + + "TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer", + "TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer", + "TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer", + "TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer", + "SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer", + "SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer", +} +''' \ No newline at end of file diff --git a/WellService/urls.py b/WellService/urls.py index 0bdf5cc..f347962 100644 --- a/WellService/urls.py +++ b/WellService/urls.py @@ -17,7 +17,14 @@ Including another URLconf from django.contrib import admin from django.urls import path, include +from rest_framework_simplejwt.views import ( + TokenObtainPairView, + TokenRefreshView, + TokenVerifyView, +) + urlpatterns = [ path('admin/', admin.site.urls), - path('proxy/wells/', include('proxy.urls')), + path('wells/', include('proxy.urls')), + path('token/', include('jwtauth.urls')), ] diff --git a/jwtauth/__init__.py b/jwtauth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jwtauth/admin.py b/jwtauth/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/jwtauth/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/jwtauth/apps.py b/jwtauth/apps.py new file mode 100644 index 0000000..f9558ca --- /dev/null +++ b/jwtauth/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class JwtauthConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'jwtauth' diff --git a/jwtauth/migrations/0001_initial.py b/jwtauth/migrations/0001_initial.py new file mode 100644 index 0000000..6b3d9fe --- /dev/null +++ b/jwtauth/migrations/0001_initial.py @@ -0,0 +1,22 @@ +# Generated by Django 5.0.7 on 2024-08-19 12:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='OkService', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=30)), + ('province', models.CharField(max_length=30)), + ], + ), + ] diff --git a/jwtauth/migrations/__init__.py b/jwtauth/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jwtauth/models.py b/jwtauth/models.py new file mode 100644 index 0000000..3bf9644 --- /dev/null +++ b/jwtauth/models.py @@ -0,0 +1,7 @@ +from django.db import models + +# Create your models here. + +class OkService(models.Model): + name = models.CharField(max_length=30) + province = models.CharField(max_length=30) \ No newline at end of file diff --git a/jwtauth/tests.py b/jwtauth/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/jwtauth/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/jwtauth/urls.py b/jwtauth/urls.py new file mode 100644 index 0000000..858dcbf --- /dev/null +++ b/jwtauth/urls.py @@ -0,0 +1,20 @@ +from django.urls import path + +from rest_framework_simplejwt.views import ( + TokenObtainPairView, + TokenRefreshView, + TokenVerifyView, +) + +from .views import * + +urlpatterns = [ + #to do: costumize this! + path('register/', register_service, name='register_service'), + #post username and password, get access and refresh token + path('', TokenObtainPairView.as_view(), name='token_obtain_pair'), + #post refresh token to this url to get a new access token + path('refresh/', TokenRefreshView.as_view(), name='token_refresh'), + #validates the access token + path('verify/', TokenVerifyView.as_view(), name='token_verify'), +] diff --git a/jwtauth/views.py b/jwtauth/views.py new file mode 100644 index 0000000..cb95252 --- /dev/null +++ b/jwtauth/views.py @@ -0,0 +1,43 @@ +from django.shortcuts import render + +from rest_framework.decorators import api_view +from rest_framework_simplejwt.tokens import RefreshToken +from rest_framework_simplejwt.exceptions import InvalidToken +from rest_framework_simplejwt.tokens import UntypedToken +import json + +from .models import OkService + + +@api_view(['POST']) +def register_service(request): + try: + data = dict(json.loads(request.body.decode('utf-8'))) + print(data, type(data)) + if "shared_key" in dict(data).keys: + if first_step_check(): + ... + + services = OkService.objects.all() + except Exception as ex: + print(ex) + +def first_step_check(shared_key): + service_id = 1 + return service_id + + +def generate_tokens(service_id): + refresh = RefreshToken.for_user(service_id) + return { + 'refresh': str(refresh), + 'access': str(refresh.access_token), + } + + +def validate_token(token): + try: + UntypedToken(token) + return True + except InvalidToken: + return False \ No newline at end of file diff --git a/proxy/views.py b/proxy/views.py index 8e8a658..5e50fb9 100644 --- a/proxy/views.py +++ b/proxy/views.py @@ -7,7 +7,9 @@ from django.core import serializers from rest_framework.response import Response from rest_framework import status -from rest_framework.decorators import api_view +from rest_framework.decorators import api_view, permission_classes +from rest_framework.permissions import IsAuthenticated + import json import requests @@ -15,12 +17,46 @@ from .models import Well from .serializers import WellSerializer +#To optain the jwt Token for the first time from other services +''' +import requests +url = 'http:///token/' + +credentials = { + "username": "your_username", + "password": "your_password" +} + +response = requests.post(url, data=credentials) +token = response.json().get('access') +''' +# Store this token to use in future requests +''' +headers = { + 'Authorization': f'Bearer {token}', +} +response = requests.post('http:///<...>/', headers=headers, json=data_to_send) +''' + +#error_list +''' +{ + "200_created": ({"data": "", "message": ""}, status=status.HTTP_200_OK), + "200_updated": ({"data": "", "message": f""}, status=status.HTTP_200_OK), + "400":({"message": serializer.errors}, status=status.HTTP_400_BAD_REQUEST), + "404": ({"message": ""}, status=status.HTTP_404_NOT_FOUND), + "409": ({"message":f'<{data["license_code"]} already exists>'}, status=status.HTTP_409_CONFLICT), + "500": ({"message": ""}, status=status.HTTP_500_INTERNAL_SERVER_ERROR), +} +''' + @api_view(['POST']) +@permission_classes([IsAuthenticated]) def create_well(request): try: data = json.loads(request.body.decode('utf-8')) - instance = Well.objects.get( - license_code=data["license_code"]) + instance = Well.objects.filter( + license_code=data["license_code"], log_type = 0) if instance: return Response( {"message":f'{data["license_code"]} already exists'}, @@ -29,33 +65,38 @@ def create_well(request): serializer = WellSerializer(data=data) if serializer.is_valid(): serializer.save() + dd return Response( - serializer.data, + {"data":serializer.data}, status=status.HTTP_201_CREATED ) else: return Response( - serializer.errors, + {"message":serializer.errors}, status=status.HTTP_400_BAD_REQUEST ) except Exception as e: print(e) - return Response({"error": "Internal Server Error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response({"message": "Internal Server Error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) @api_view(['GET']) +@permission_classes([IsAuthenticated]) def send_well_by_well_id(request, obj_id): try: well = Well.objects.filter(id=obj_id).first() if well is None: - return Response({"error": "Not Found"}, status=status.HTTP_404_NOT_FOUND) + return Response({"message": "Not Found"}, status=status.HTTP_404_NOT_FOUND) serializer = WellSerializer(well) - return Response(serializer.data, status=status.HTTP_200_OK) + return Response({"data":serializer.data}, status=status.HTTP_200_OK) except Exception as ex: print(ex) - return Response({"error": "Internal Server Error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response({"message": "Internal Server Error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + @api_view(['GET']) +@permission_classes([IsAuthenticated]) def send_wells(request): + #permission_classes = [IsAuthenticated] try: # to do: add owner # to do: add filter @@ -63,24 +104,28 @@ def send_wells(request): #‌ to do: add no return data wells = Well.objects.all() serializer = WellSerializer(wells, many=True) - return Response(serializer.data, status=status.HTTP_200_OK) + return Response({"data":serializer.data}, status=status.HTTP_200_OK) except Exception as ex: print(ex) - return Response({"error": "Internal Server Error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response({"message": "Internal Server Error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +@permission_classes([IsAuthenticated]) @api_view(['DELETE']) def delete_well_by_well_id(request, obj_id): try: instance = Well.objects.get(id=obj_id, log_type=0) instance.log_type = 1 instance.save() - return Response({"detail": f"Successfully updated log type for well {instance.license_code}"}, status=status.HTTP_200_OK) + return Response({"message": f"Successfully removed log type for well {instance.license_code}"}, status=status.HTTP_200_OK) except Well.DoesNotExist: - return Response({"error": "Not Found"}, status=status.HTTP_404_NOT_FOUND) + return Response({"message": "Not Found"}, status=status.HTTP_404_NOT_FOUND) except Exception as e: print(e) - return Response({"error": "Internal Server Error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response({"message": "Internal Server Error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +@permission_classes([IsAuthenticated]) @api_view(['PUT']) def update_well_by_well_id(request, obj_id): try: @@ -94,13 +139,15 @@ def update_well_by_well_id(request, obj_id): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) else: - return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response({"message":serializer.errors}, status=status.HTTP_400_BAD_REQUEST) except Well.DoesNotExist: return Response({"error": "Not Found"}, status=status.HTTP_404_NOT_FOUND) except Exception as e: print(e) return Response({"error": "Internal Server Error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + +@permission_classes([IsAuthenticated]) @api_view(['PUT']) def edit_well_by_well_id(request, obj_id): try: