diff --git a/.vscode/settings.json b/.vscode/settings.json index cc67606..d1491e2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,13 @@ { "python.linting.pylintEnabled": true, - "python.linting.enabled": true + "python.linting.enabled": true, + "python.linting.pylintArgs": [ + "--django-settings-module=portalusers.settings", + "--load-plugins=pylint_django", + ], + "django.snippets.exclude": [ + + "cms", + "wagtail" + ] } \ No newline at end of file diff --git a/core/apps.py b/core/apps.py index 5b00e97..a48aca4 100644 --- a/core/apps.py +++ b/core/apps.py @@ -7,5 +7,5 @@ class CoreConfig(AppConfig): """Core""" - name = 'core' - default_auto_field = 'django.db.models.AutoField' + name = "core" + default_auto_field = "django.db.models.AutoField" diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py index 66c3fdd..03e7e93 100644 --- a/core/migrations/0001_initial.py +++ b/core/migrations/0001_initial.py @@ -15,26 +15,58 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Profile', + name="Profile", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('public', models.BooleanField(blank=True, default=False)), - ('affiliation', models.CharField(blank=True, max_length=1000)), - ('orcid', models.CharField(blank=True, max_length=1000)), - ('username', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("public", models.BooleanField(blank=True, default=False)), + ("affiliation", models.CharField(blank=True, max_length=1000)), + ("orcid", models.CharField(blank=True, max_length=1000)), + ( + "username", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='ApiInfo', + name="ApiInfo", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('username', models.CharField(blank=True, max_length=1000)), - ('hostname', models.CharField(blank=True, max_length=15)), - ('human_readable_hostname', models.CharField(blank=True, max_length=1000)), - ('public_hostname', models.CharField(blank=True, max_length=1000)), - ('token', models.CharField(blank=True, max_length=1000)), - ('other_info', models.JSONField()), - ('local_username', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='custom_user', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("username", models.CharField(blank=True, max_length=1000)), + ("hostname", models.CharField(blank=True, max_length=15)), + ( + "human_readable_hostname", + models.CharField(blank=True, max_length=1000), + ), + ("public_hostname", models.CharField(blank=True, max_length=1000)), + ("token", models.CharField(blank=True, max_length=1000)), + ("other_info", models.JSONField()), + ( + "local_username", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="custom_user", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/core/migrations/0002_prefixes.py b/core/migrations/0002_prefixes.py index 94b8b27..b413ac7 100644 --- a/core/migrations/0002_prefixes.py +++ b/core/migrations/0002_prefixes.py @@ -6,18 +6,26 @@ class Migration(migrations.Migration): dependencies = [ - ('core', '0001_initial'), + ("core", "0001_initial"), ] operations = [ migrations.CreateModel( - name='Prefixes', + name="Prefixes", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('username', models.CharField(max_length=100)), - ('prefix', models.CharField(max_length=5)), - ('registration_date', models.DateTimeField()), - ('registration_certificate', models.CharField(max_length=1000)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("username", models.CharField(max_length=100)), + ("prefix", models.CharField(max_length=5)), + ("registration_date", models.DateTimeField()), + ("registration_certificate", models.CharField(max_length=1000)), ], ), ] diff --git a/core/migrations/0003_auto_20220330_2122.py b/core/migrations/0003_auto_20220330_2122.py index 634bb00..fb186d7 100644 --- a/core/migrations/0003_auto_20220330_2122.py +++ b/core/migrations/0003_auto_20220330_2122.py @@ -6,17 +6,19 @@ class Migration(migrations.Migration): dependencies = [ - ('core', '0002_prefixes'), + ("core", "0002_prefixes"), ] operations = [ migrations.RemoveField( - model_name='prefixes', - name='id', + model_name="prefixes", + name="id", ), migrations.AlterField( - model_name='prefixes', - name='prefix', - field=models.CharField(max_length=5, primary_key=True, serialize=False, unique=True), + model_name="prefixes", + name="prefix", + field=models.CharField( + max_length=5, primary_key=True, serialize=False, unique=True + ), ), ] diff --git a/core/migrations/0004_auto_20220716_1651.py b/core/migrations/0004_auto_20220716_1651.py index 9b767cb..a4d9af8 100644 --- a/core/migrations/0004_auto_20220716_1651.py +++ b/core/migrations/0004_auto_20220716_1651.py @@ -9,13 +9,17 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('core', '0003_auto_20220330_2122'), + ("core", "0003_auto_20220330_2122"), ] operations = [ migrations.AlterField( - model_name='prefixes', - name='username', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field='username'), + model_name="prefixes", + name="username", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + to_field="username", + ), ), ] diff --git a/core/models.py b/core/models.py index bf87a71..e091216 100644 --- a/core/models.py +++ b/core/models.py @@ -12,33 +12,42 @@ from rest_framework import status from rest_framework.response import Response + @receiver(reset_password_token_created) -def password_reset_token_created(sender, instance, reset_password_token, *args, **kwargs): +def password_reset_token_created( + sender, instance, reset_password_token, *args, **kwargs +): """ Create the token for a password reset. """ - email_plaintext_message = "{}?token={}".format(reverse( - 'password_reset:reset-password-request'), reset_password_token.key) + email_plaintext_message = "{}?token={}".format( + reverse("password_reset:reset-password-request"), reset_password_token.key + ) try: send_mail( - subject='Password reset for BioCompute Portal', - message= email_plaintext_message, + subject="Password reset for BioCompute Portal", + message=email_plaintext_message, html_message=email_plaintext_message, - from_email='mail_sender@portal.aws.biochemistry.gwu.edu', + from_email="mail_sender@portal.aws.biochemistry.gwu.edu", recipient_list=[reset_password_token.user.email], fail_silently=False, ) except Exception as error: - print('activation_link', reset_password_token) + print("activation_link", reset_password_token) # print('ERROR: ', error) # TODO: Should handle when the send_mail function fails? # return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR, data={ # "message": "Not able to send authentication email: {}".format(error)}) - return Response(status=status.HTTP_201_CREATED, - data={"message": "Reset token has been requested but email was not sent."\ - f" Check with your database administrator for your token. {error}"}) + return Response( + status=status.HTTP_201_CREATED, + data={ + "message": "Reset token has been requested but email was not sent." + f" Check with your database administrator for your token. {error}" + }, + ) + class Profile(models.Model): """Profile @@ -55,14 +64,16 @@ class Profile(models.Model): orcid: User ORCID """ + username = models.OneToOneField(User, on_delete=models.CASCADE) - public = models.BooleanField(blank = True, default=False) - affiliation = models.CharField(blank = True, max_length = 1000) - orcid = models.CharField(blank = True, max_length = 1000) + public = models.BooleanField(blank=True, default=False) + affiliation = models.CharField(blank=True, max_length=1000) + orcid = models.CharField(blank=True, max_length=1000) def __str__(self): """String for representing the Profile model (in Admin site etc.).""" - return str(f'{self.username}') + return str(f"{self.username}") + class ApiInfo(models.Model): """API Information @@ -86,20 +97,19 @@ class ApiInfo(models.Model): """ local_username = models.ForeignKey( - User, - on_delete = models.CASCADE, - related_name = 'custom_user' + User, on_delete=models.CASCADE, related_name="custom_user" ) - username = models.CharField(blank = True, max_length = 1000) - hostname = models.CharField(blank = True, max_length = 15) - human_readable_hostname = models.CharField(blank = True, max_length = 1000) - public_hostname = models.CharField(blank = True, max_length = 1000) - token = models.CharField(blank = True, max_length = 1000) + username = models.CharField(blank=True, max_length=1000) + hostname = models.CharField(blank=True, max_length=15) + human_readable_hostname = models.CharField(blank=True, max_length=1000) + public_hostname = models.CharField(blank=True, max_length=1000) + token = models.CharField(blank=True, max_length=1000) other_info = models.JSONField() def __str__(self): """String for representing the ApiInfo model (in Admin site etc.).""" - return str(f'{self.username} at {self.hostname}') + return str(f"{self.username} at {self.hostname}") + class Prefixes(models.Model): """Prefix Table: core_prefixes @@ -112,14 +122,10 @@ class Prefixes(models.Model): registration_certificate: str """ - prefix = models.CharField(max_length = 5, primary_key=True, unique=True) - username = models.ForeignKey( - User, - on_delete=models.CASCADE, - to_field="username" - ) + prefix = models.CharField(max_length=5, primary_key=True, unique=True) + username = models.ForeignKey(User, on_delete=models.CASCADE, to_field="username") registration_date = models.DateTimeField() - registration_certificate = models.CharField(max_length = 1000) + registration_certificate = models.CharField(max_length=1000) def __str__(self): """String for representing the Prefix (in Admin site etc.).""" diff --git a/core/serializers.py b/core/serializers.py index 700c280..f1725a5 100644 --- a/core/serializers.py +++ b/core/serializers.py @@ -11,6 +11,7 @@ # Source: https://stackoverflow.com/questions/33844003/ # how-to-serialize-groups-of-a-user-with-django-rest-framework/33844179 + class ChangePasswordSerializer(serializers.Serializer): """Serializer for password change endpoint. @@ -24,6 +25,7 @@ class ChangePasswordSerializer(serializers.Serializer): old_password = serializers.CharField(required=True) new_password = serializers.CharField(required=True) + # Profile serializer class ProfileSerializer(serializers.ModelSerializer): """Profile serializer @@ -33,8 +35,9 @@ class ProfileSerializer(serializers.ModelSerializer): class Meta: """Meta""" + model = Profile - fields = ('username', 'public', 'affiliation', 'orcid') + fields = ("username", "public", "affiliation", "orcid") # API serializer @@ -46,48 +49,54 @@ class ApiSerializer(serializers.ModelSerializer): class Meta: """Meta""" + model = ApiInfo fields = ( - 'username', - 'hostname', - 'human_readable_hostname', - 'public_hostname', - 'token', - 'other_info' + "username", + "hostname", + "human_readable_hostname", + "public_hostname", + "token", + "other_info", ) class GroupSerializer(serializers.ModelSerializer): """Group Serializer""" + class Meta: """Meta""" + model = Group - fields = ('name',) + fields = ("name",) class UserSerializer(serializers.ModelSerializer): """User Serializer""" - apiinfo = ApiSerializer(source='custom_user', many=True) + + apiinfo = ApiSerializer(source="custom_user", many=True) groups = GroupSerializer(many=True) profile = ProfileSerializer(many=False) class Meta: """Meta""" + model = User fields = ( - 'username', - 'password', - 'first_name', - 'last_name', - 'email', - 'profile', - 'groups', - 'apiinfo' + "username", + "password", + "first_name", + "last_name", + "email", + "profile", + "groups", + "apiinfo", ) class UserSerializerWithToken(serializers.ModelSerializer): """User Serializer""" + token = serializers.SerializerMethodField() password = serializers.CharField(write_only=True) @@ -102,9 +111,8 @@ def get_token(self, obj): return token def create(self, validated_data): - """Create - """ - password = validated_data.pop('password', None) + """Create""" + password = validated_data.pop("password", None) instance = self.Meta.model(**validated_data) if password is not None: @@ -116,4 +124,12 @@ def create(self, validated_data): class Meta: "Meta" model = User - fields = ('token', 'username', 'password', 'first_name', 'last_name', 'email', 'groups',) + fields = ( + "token", + "username", + "password", + "first_name", + "last_name", + "email", + "groups", + ) diff --git a/core/tests/test_models.py b/core/tests/test_models.py index a2c35db..cf068f7 100644 --- a/core/tests/test_models.py +++ b/core/tests/test_models.py @@ -7,9 +7,14 @@ from django.contrib.auth.models import User from django.utils import timezone from django.urls import reverse -from core.serializers import UserSerializer, UserSerializerWithToken, ChangePasswordSerializer +from core.serializers import ( + UserSerializer, + UserSerializerWithToken, + ChangePasswordSerializer, +) from core.models import Profile + class UserTests(TestCase): """Test for User Creation @@ -17,50 +22,45 @@ class UserTests(TestCase): """ def create_user(self): - """Creat Test User - - """ + """Creat Test User""" user_request = { - 'username': 'tester', - 'email': 'test@testing.com', - 'password': 'testing123', - 'profile': { - 'username': 'tester', - 'public': True, - 'affiliation': 'Testing', - 'orcid': 'https://orcid.org/xxxx-xxxx-xxxx-xxxx' - } + "username": "tester", + "email": "test@testing.com", + "password": "testing123", + "profile": { + "username": "tester", + "public": True, + "affiliation": "Testing", + "orcid": "https://orcid.org/xxxx-xxxx-xxxx-xxxx", + }, } - profile = user_request['profile'] - del user_request['profile'] + profile = user_request["profile"] + del user_request["profile"] serializer = UserSerializerWithToken(data=user_request) if serializer.is_valid(): serializer.save() - user_object = User.objects.get(username=user_request['username']) + user_object = User.objects.get(username=user_request["username"]) Profile.objects.create( username=user_object, - public=profile['public'], - affiliation=profile['affiliation'], - orcid=profile['orcid']) + public=profile["public"], + affiliation=profile["affiliation"], + orcid=profile["orcid"], + ) - return User.objects.get(username=user_request['username']) + return User.objects.get(username=user_request["username"]) return None def test_user(self): - """Tests for User - - """ + """Tests for User""" user = self.create_user() self.assertTrue(isinstance(user, User)) - self.assertEqual(user.email, 'test@testing.com') + self.assertEqual(user.email, "test@testing.com") def test_profile(self): - """Tests for Profile - - """ + """Tests for Profile""" user = self.create_user() profile = Profile.objects.get(username=user.id) self.assertTrue(isinstance(profile, Profile)) diff --git a/core/urls.py b/core/urls.py index 0ebf92b..b39f659 100644 --- a/core/urls.py +++ b/core/urls.py @@ -6,7 +6,11 @@ from django.urls import path, include from django.contrib.staticfiles.storage import staticfiles_storage from django.views.generic.base import RedirectView -from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token +from rest_framework_jwt.views import ( + obtain_jwt_token, + refresh_jwt_token, + verify_jwt_token, +) from core.views import ( current_user, add_api, @@ -15,24 +19,27 @@ update_user, ChangePasswordView, register_prefix, - SearchPrefix + SearchPrefix, ) urlpatterns = [ - path('favicon.ico', RedirectView.as_view(url=staticfiles_storage.url('img/favicon.ico'))), - path('users/current_user/', current_user), - path('users/add_api/', add_api), - path('users/remove_api/', remove_api), - path('users/list/', CreateUser.as_view()), - path('users/update_user/', update_user), - path('users/token-auth/', obtain_jwt_token), - path('users/token-refresh/', refresh_jwt_token), - path('users/token-verify/', verify_jwt_token), - path('users/change_password/', ChangePasswordView.as_view()), - path('users/password_reset/', include( - 'django_rest_passwordreset.urls', - namespace='password_reset') + path( + "favicon.ico", + RedirectView.as_view(url=staticfiles_storage.url("img/favicon.ico")), + ), + path("users/current_user/", current_user), + path("users/add_api/", add_api), + path("users/remove_api/", remove_api), + path("users/list/", CreateUser.as_view()), + path("users/update_user/", update_user), + path("users/token-auth/", obtain_jwt_token), + path("users/token-refresh/", refresh_jwt_token), + path("users/token-verify/", verify_jwt_token), + path("users/change_password/", ChangePasswordView.as_view()), + path( + "users/password_reset/", + include("django_rest_passwordreset.urls", namespace="password_reset"), ), - path('users/register_prefix/', register_prefix), - path('users/prefixes/', SearchPrefix.as_view()), + path("users/register_prefix/", register_prefix), + path("users/prefixes/", SearchPrefix.as_view()), ] diff --git a/core/views.py b/core/views.py index 47a485f..b145248 100644 --- a/core/views.py +++ b/core/views.py @@ -2,13 +2,12 @@ """Views """ -from email import header from itertools import chain import json from sys import prefix import uuid -import requests from datetime import datetime +import requests from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema from rest_framework import permissions, status, generics @@ -16,13 +15,17 @@ from rest_framework.response import Response from rest_framework.views import APIView from django.core.exceptions import ValidationError -from django.http import HttpResponse from django.contrib.auth.models import User from rest_framework_jwt.views import VerifyJSONWebTokenSerializer from core.models import ApiInfo, Profile, Prefixes -from .serializers import UserSerializer, UserSerializerWithToken, ChangePasswordSerializer +from .serializers import ( + UserSerializer, + UserSerializerWithToken, + ChangePasswordSerializer, +) from django.core.exceptions import ObjectDoesNotExist + class CreateUser(APIView): """Create a new user @@ -34,70 +37,94 @@ class CreateUser(APIView): type=openapi.TYPE_OBJECT, title="Account Creation Schema", description="Account creation schema description.", - required=['username', 'email', 'password'], + required=["username", "email", "password"], properties={ - 'username': openapi.Schema(type=openapi.TYPE_STRING, - description='Hostname of the User Database.'), - 'email' : openapi.Schema(type=openapi.TYPE_STRING, - description='Email address of user.'), - 'password': openapi.Schema(type=openapi.TYPE_STRING, - description='Token returned with new user being '), - 'profile' : openapi.Schema( + "username": openapi.Schema( + type=openapi.TYPE_STRING, description="Hostname of the User Database." + ), + "email": openapi.Schema( + type=openapi.TYPE_STRING, description="Email address of user." + ), + "password": openapi.Schema( + type=openapi.TYPE_STRING, + description="Token returned with new user being ", + ), + "profile": openapi.Schema( type=openapi.TYPE_OBJECT, - description='Token returned with new user being ', - required=['username'], + description="Token returned with new user being ", + required=["username"], properties={ - 'username': openapi.Schema(type=openapi.TYPE_STRING, - description='Username for the profile user object. Should be the same as above.'), - 'public' : openapi.Schema(type=openapi.TYPE_BOOLEAN, - description='Boolean to indicate if this users profile is publicly viewable.'), - 'affiliation': openapi.Schema(type=openapi.TYPE_STRING, - description='Affiliation of the User.'), - 'orcid': openapi.Schema(type=openapi.TYPE_STRING, - description='ORCID for the User.') - } ), - }) - - @swagger_auto_schema(request_body=request_body, responses={ + "username": openapi.Schema( + type=openapi.TYPE_STRING, + description="Username for the profile user object. Should be the same as above.", + ), + "public": openapi.Schema( + type=openapi.TYPE_BOOLEAN, + description="Boolean to indicate if this users profile is publicly viewable.", + ), + "affiliation": openapi.Schema( + type=openapi.TYPE_STRING, description="Affiliation of the User." + ), + "orcid": openapi.Schema( + type=openapi.TYPE_STRING, description="ORCID for the User." + ), + }, + ), + }, + ) + + @swagger_auto_schema( + request_body=request_body, + responses={ 200: "Account creation is successful.", 400: "Bad request.", 403: "Invalid token.", 409: "Account has already been authenticated or requested.", - 500: "Unable to save the new account or send authentication email." - }, tags=["Account Management"]) - + 500: "Unable to save the new account or send authentication email.", + }, + tags=["Account Management"], + ) def post(self, request): """doc""" - print('request.data: ') - print('USERNAME: ', request.data['username'], 'EMAIL: ', request.data['email'], 'PASSWORD') + print("request.data: ") + print( + "USERNAME: ", + request.data["username"], + "EMAIL: ", + request.data["email"], + "PASSWORD", + ) # Does this user already exist? - if User.objects.filter(username = request.data['username']).exists(): + if User.objects.filter(username=request.data["username"]).exists(): # Bad request because the user already exists. return Response(status=status.HTTP_409_CONFLICT) else: - profile_object = request.data['profile'] - del request.data['profile'] + profile_object = request.data["profile"] + del request.data["profile"] serializer = UserSerializerWithToken(data=request.data) if serializer.is_valid(): serializer.save() - user_object = User.objects.get(username=request.data['username']) + user_object = User.objects.get(username=request.data["username"]) Profile.objects.create( - username=user_object, - public=profile_object['public'], - affiliation=profile_object['affiliation'], - orcid=profile_object['orcid']) + username=user_object, + public=profile_object["public"], + affiliation=profile_object["affiliation"], + orcid=profile_object["orcid"], + ) return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + class ChangePasswordView(generics.UpdateAPIView): """ An endpoint for changing password. """ + serializer_class = ChangePasswordSerializer model = User @@ -106,22 +133,29 @@ class ChangePasswordView(generics.UpdateAPIView): type=openapi.TYPE_OBJECT, title="Password Change Schema", description="Endpoint for changing password.", - required=['old_password', 'new_password'], + required=["old_password", "new_password"], properties={ - 'old_password': openapi.Schema(type=openapi.TYPE_STRING, - description='Token returned with new user being '), - 'new_password': openapi.Schema(type=openapi.TYPE_STRING, - description='Token returned with new user being ') - } + "old_password": openapi.Schema( + type=openapi.TYPE_STRING, + description="Token returned with new user being ", + ), + "new_password": openapi.Schema( + type=openapi.TYPE_STRING, + description="Token returned with new user being ", + ), + }, ) - @swagger_auto_schema(request_body=request_body, responses={ + @swagger_auto_schema( + request_body=request_body, + responses={ 200: "Password updated successfully.", 400: "Bad request.", 401: "Invalid username/password.", - 500: "Server Error" - }, tags=["Account Management"]) - + 500: "Server Error", + }, + tags=["Account Management"], + ) def get_object(self, queryset=None): """Get Object""" obj = self.request.user @@ -135,39 +169,44 @@ def update(self, request, *args, **kwargs): if serializer.is_valid(): # Check old password if not self.object.check_password(serializer.data.get("old_password")): - return Response({"old_password": ["Wrong password."]}, status=status.HTTP_400_BAD_REQUEST) + return Response( + {"old_password": ["Wrong password."]}, + status=status.HTTP_400_BAD_REQUEST, + ) # set_password also hashes the password that the user will get self.object.set_password(serializer.data.get("new_password")) self.object.save() response = { - 'status': 'success', - 'code': status.HTTP_200_OK, - 'message': 'Password updated successfully', - 'data': [] + "status": "success", + "code": status.HTTP_200_OK, + "message": "Password updated successfully", + "data": [], } return Response(response) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + @swagger_auto_schema(method="get", tags=["Account Management"]) -@api_view(['GET']) +@api_view(["GET"]) def current_user(request): """ Determine the current user by their token, and return their data """ - print('HERE') + print("HERE") serializer = UserSerializer(request.user) - print('+++++++++++++++') + print("+++++++++++++++") print(serializer.data) - print('+++++++++++++++') + print("+++++++++++++++") return Response(serializer.data) + @swagger_auto_schema(method="delete", tags=["API Management"]) -@api_view(['DELETE']) +@api_view(["DELETE"]) def remove_api(request): """ Remove API information @@ -175,9 +214,9 @@ def remove_api(request): """ # Get the user. - print('U check') + print("U check") print(UserSerializer(request.user).data) - user = UserSerializer(request.user).data['username'] + user = UserSerializer(request.user).data["username"] # TODO: right way to do this? # Get the user ID so that we can link across tables. @@ -186,40 +225,44 @@ def remove_api(request): # Get the bulk information. bulk = json.loads(request.body) - for api in bulk['selected_rows']: + for api in bulk["selected_rows"]: # TODO: Should also check against the specific server token; needs to be sent from front end - result = ApiInfo.objects.filter(local_username=user_object, human_readable_hostname=api).delete() + result = ApiInfo.objects.filter( + local_username=user_object, human_readable_hostname=api + ).delete() print(result) return Response(UserSerializer(request.user).data, status=status.HTTP_200_OK) + @swagger_auto_schema(method="post", tags=["API Management"]) -@api_view(['POST']) +@api_view(["POST"]) def add_api(request): """ Update a user's information based on their token. """ # Get the user. - user = UserSerializer(request.user).data['username'] - user_object = User.objects.get(username = user) + user = UserSerializer(request.user).data["username"] + user_object = User.objects.get(username=user) # Get the bulk information. bulk = json.loads(request.body) # Add the key for the user. api_object = ApiInfo( - local_username = user_object, - username = bulk['username'], - hostname = bulk['hostname'], - human_readable_hostname = bulk['human_readable_hostname'], - public_hostname = bulk['public_hostname'], - token = bulk['token'], - other_info = bulk['other_info'] + local_username=user_object, + username=bulk["username"], + hostname=bulk["hostname"], + human_readable_hostname=bulk["human_readable_hostname"], + public_hostname=bulk["public_hostname"], + token=bulk["token"], + other_info=bulk["other_info"], ) api_object.save() - return(Response(UserSerializer(request.user).data, status=status.HTTP_201_CREATED)) + return Response(UserSerializer(request.user).data, status=status.HTTP_201_CREATED) + update_user_schema = openapi.Schema( type=openapi.TYPE_OBJECT, @@ -227,41 +270,45 @@ def add_api(request): description="Update a user's information.", required=[], properties={ - 'first_name': openapi.Schema(type=openapi.TYPE_STRING), - 'last_name': openapi.Schema(type=openapi.TYPE_STRING), - 'email': openapi.Schema(type=openapi.TYPE_STRING), - 'groups': openapi.Schema(type=openapi.TYPE_STRING), - 'password': openapi.Schema(type=openapi.TYPE_STRING), - 'username': openapi.Schema(type=openapi.TYPE_STRING), - 'affiliation': openapi.Schema(type=openapi.TYPE_STRING), - 'orcid': openapi.Schema(type=openapi.TYPE_STRING), - 'public': openapi.Schema(type=openapi.TYPE_STRING) - } + "first_name": openapi.Schema(type=openapi.TYPE_STRING), + "last_name": openapi.Schema(type=openapi.TYPE_STRING), + "email": openapi.Schema(type=openapi.TYPE_STRING), + "groups": openapi.Schema(type=openapi.TYPE_STRING), + "password": openapi.Schema(type=openapi.TYPE_STRING), + "username": openapi.Schema(type=openapi.TYPE_STRING), + "affiliation": openapi.Schema(type=openapi.TYPE_STRING), + "orcid": openapi.Schema(type=openapi.TYPE_STRING), + "public": openapi.Schema(type=openapi.TYPE_STRING), + }, +) + + +@swagger_auto_schema( + method="post", request_body=update_user_schema, tags=["Account Management"] ) -@swagger_auto_schema(method="post", request_body=update_user_schema, tags=["Account Management"]) -@api_view(['POST']) +@api_view(["POST"]) def update_user(request): """ Update a user's information. """ # Get the username - user = UserSerializer(request.user).data['username'] + user = UserSerializer(request.user).data["username"] # Get the user with associated username user_object = User.objects.get(username=user) profile_object, created = Profile.objects.get_or_create(username=user_object) bulk = json.loads(request.body) - bulk.pop('username') - if 'token' in bulk.keys(): - token = bulk.pop('token') + bulk.pop("username") + if "token" in bulk.keys(): + token = bulk.pop("token") else: token = "" for key, value in bulk.items(): - if (key == 'first_name') or (key == 'last_name') or (key == 'email'): + if (key == "first_name") or (key == "last_name") or (key == "email"): setattr(user_object, key, value) - elif (key == 'orcid') or (key == 'affiliation') or (key == 'public'): + elif (key == "orcid") or (key == "affiliation") or (key == "public"): setattr(profile_object, key, value) user_object.save() @@ -269,10 +316,7 @@ def update_user(request): profile_object.save() # properly formatted response - return Response({ - 'token': token, - 'user' : UserSerializer(request.user).data - }) + return Response({"token": token, "user": UserSerializer(request.user).data}) def search_db(value, user=None): @@ -290,29 +334,29 @@ def search_db(value, user=None): "username", "prefix", "registration_date", - "registration_certificate" - - + "registration_certificate", ] - if value == 'all': - results = list(chain( - Prefixes.objects.all() - .values(*return_values) - )) + if value == "all": + results = list(chain(Prefixes.objects.all().values(*return_values))) elif user is not None: - results = list(chain( - Prefixes.objects.filter(username_id=user.username) - .values(*return_values) - )) + results = list( + chain( + Prefixes.objects.filter(username_id=user.username).values( + *return_values + ) + ) + ) else: - results = list(chain( - Prefixes.objects.filter(prefix__icontains=value) - .values(*return_values) - )) + results = list( + chain( + Prefixes.objects.filter(prefix__icontains=value).values(*return_values) + ) + ) return results + def write_db(values): """ Arguments @@ -325,10 +369,10 @@ def write_db(values): """ writable = Prefixes( - username = values['username'], - prefix = values['prefix'].upper(), - registration_date = values['registration_date'], - registration_certificate = values['registration_certificate'], + username=values["username"], + prefix=values["prefix"].upper(), + registration_date=values["registration_date"], + registration_certificate=values["registration_certificate"], ) try: writable.full_clean() @@ -336,8 +380,9 @@ def write_db(values): except ValidationError as error: return error + @swagger_auto_schema(method="post", tags=["Prefix Management"]) -@api_view(['POST']) +@api_view(["POST"]) def register_prefix(request): """ Base the response on the request method. @@ -357,34 +402,34 @@ def register_prefix(request): ``` """ - bulk_request = request.data['POST_register_prefix'] + bulk_request = request.data["POST_register_prefix"] return_data = [] any_failed = False for new_prefix in bulk_request: try: - user = User.objects.get(username=new_prefix['username']) + user = User.objects.get(username=new_prefix["username"]) except ObjectDoesNotExist: return_data.append( { - "request_status":"FAILURE", - "status_code":"401", - "message":"The username provided does not match the user DB. "+ \ - "Please login or create an account and re-submit." + "request_status": "FAILURE", + "status_code": "401", + "message": "The username provided does not match the user DB. " + + "Please login or create an account and re-submit.", } ) any_failed = True - results = list(chain( - Prefixes.objects.filter(prefix=new_prefix['prefix']) - )) + results = list(chain(Prefixes.objects.filter(prefix=new_prefix["prefix"]))) if len(results) == 0: - api_object= ApiInfo.objects.get(local_username=user, human_readable_hostname='BCO Server (Default)') - if new_prefix['public'] == 'false': + api_object = ApiInfo.objects.get( + local_username=user, human_readable_hostname="BCO Server (Default)" + ) + if new_prefix["public"] == "false": owner_group = api_object.username else: - owner_group = 'bco_drafter' + owner_group = "bco_drafter" owner_user = api_object.username headers = { "Authorization": "Token " + api_object.token, @@ -392,71 +437,79 @@ def register_prefix(request): } bco_api = requests.post( - data=json.dumps({ - 'POST_api_prefixes_create': [ + data=json.dumps( { - 'owner_group': owner_group, - 'owner_user': owner_user, - "prefixes": [ - { - 'description': new_prefix['description'], - 'prefix': new_prefix['prefix'] - } - ] + "POST_api_prefixes_create": [ + { + "owner_group": owner_group, + "owner_user": owner_user, + "prefixes": [ + { + "description": new_prefix["description"], + "prefix": new_prefix["prefix"], + } + ], + } + ] } - ] - }), + ), headers=headers, - url=api_object.public_hostname + '/api/prefixes/create/' + url=api_object.public_hostname + "/api/prefixes/create/", ) if bco_api.status_code != 200: return_data.append(bco_api.json()[0]) any_failed = True continue - if write_db({ - 'username': user, - 'prefix': new_prefix['prefix'], - 'registration_date': datetime.now().strftime("%Y-%m-%d %H:%M:%S"), - 'registration_certificate': uuid.uuid4().hex - }) is not None: + if ( + write_db( + { + "username": user, + "prefix": new_prefix["prefix"], + "registration_date": datetime.now().strftime( + "%Y-%m-%d %H:%M:%S" + ), + "registration_certificate": uuid.uuid4().hex, + } + ) + is not None + ): return_data.append( { - "request_status":"FAILURE", - "status_code":"400", - "message":f"The {prefix} provided does not match the format required." + "request_status": "FAILURE", + "status_code": "400", + "message": f"The {prefix} provided does not match the format required.", } ) any_failed = True return_data.append( { - "request_status":"SUCCESS", - "status_code":"201", - "message":f"The Prefix {prefix} provided was " + \ - f"successfullyregistered for {owner_user}." + "request_status": "SUCCESS", + "status_code": "201", + "message": f"The Prefix {prefix} provided was " + + f"successfullyregistered for {owner_user}.", } ) else: return_data.append( { - "request_status":"FAILURE", - "status_code":"409", - "message":f"The Prefix {prefix} provided is not available." + "request_status": "FAILURE", + "status_code": "409", + "message": f"The Prefix {prefix} provided is not available.", } ) any_failed = True if any_failed: return Response(status=status.HTTP_207_MULTI_STATUS, data=return_data) - + return Response(status=status.HTTP_200_OK, data=return_data) -class SearchPrefix(APIView): - """Search Prefix DB - """ +class SearchPrefix(APIView): + """Search Prefix DB""" authentication_classes = [] permission_classes = [] @@ -468,25 +521,22 @@ class SearchPrefix(APIView): required=[], properties={ "search_term": openapi.Schema( - type=openapi.TYPE_STRING, - description='Search term' + type=openapi.TYPE_STRING, description="Search term" ), "search_type": openapi.Schema( - type=openapi.TYPE_STRING, - description="Search type" - ) - } + type=openapi.TYPE_STRING, description="Search type" + ), + }, ) request_body = openapi.Schema( type=openapi.TYPE_OBJECT, title="Prefix Search", - required=['post_userdb_prefix_search_schema'], + required=["post_userdb_prefix_search_schema"], properties={ - 'post_userdb_prefix_search_schema': openapi.Schema( - type=openapi.TYPE_ARRAY, - items=post_userdb_prefix_search_schema + "post_userdb_prefix_search_schema": openapi.Schema( + type=openapi.TYPE_ARRAY, items=post_userdb_prefix_search_schema ) - } + }, ) @swagger_auto_schema( @@ -494,34 +544,33 @@ class SearchPrefix(APIView): responses={ 200: "Search results", 404: "object not found", - 500: "invalid search" + 500: "invalid search", }, - tags=["Prefix Management"] + tags=["Prefix Management"], ) - def post(self, request): """Post""" - search_list = request.data['post_userdb_prefix_search'] + search_list = request.data["post_userdb_prefix_search"] for item in search_list: - search_type = item['search_type'] - search_term = item['search_term'] - if search_type == 'mine': - token = request.META.get('HTTP_AUTHORIZATION', " ").split(' ')[1] - data = {'token': token} + search_type = item["search_type"] + search_term = item["search_term"] + if search_type == "mine": + token = request.META.get("HTTP_AUTHORIZATION", " ").split(" ")[1] + data = {"token": token} try: valid_data = VerifyJSONWebTokenSerializer().validate(data) - user = valid_data['user'] + user = valid_data["user"] request.user = user except ValidationError as error: print("validation error", error) - prefix_results = search_db(value='MINE', user=user) - if search_type == 'search' and search_term is not None: + prefix_results = search_db(value="MINE", user=user) + if search_type == "search" and search_term is not None: prefix_results = search_db(value=search_term.upper()) - if search_type == 'all': - prefix_results = search_db(value='all') - if search_term is None and search_type == 'search': - prefix_results = search_db(value='all') - if search_term == '' and search_type == 'search': - prefix_results = search_db(value='all') + if search_type == "all": + prefix_results = search_db(value="all") + if search_term is None and search_type == "search": + prefix_results = search_db(value="all") + if search_term == "" and search_type == "search": + prefix_results = search_db(value="all") # prefix_results = search_db(value='all') return Response(data=prefix_results, status=status.HTTP_200_OK) diff --git a/docs/source/conf.py b/docs/source/conf.py index 89c2cfe..e0cdc96 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -17,9 +17,9 @@ # -- Project information ----------------------------------------------------- -project = 'BCO Portal UserDB' -copyright = '2021, BioCOmpute' -author = 'BioCOmpute' +project = "BCO Portal UserDB" +copyright = "2021, BioCOmpute" +author = "BioCOmpute" # -- General configuration --------------------------------------------------- @@ -27,11 +27,10 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ -] +extensions = [] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -44,9 +43,9 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = "alabaster" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] \ No newline at end of file +html_static_path = ["_static"] diff --git a/manage.py b/manage.py index bfb478d..51cb63b 100755 --- a/manage.py +++ b/manage.py @@ -6,7 +6,7 @@ def main(): """Run administrative tasks.""" - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'portalusers.settings') + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "portalusers.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: @@ -18,5 +18,5 @@ def main(): execute_from_command_line(sys.argv) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/portalusers/asgi.py b/portalusers/asgi.py index ad461b7..badb2d3 100644 --- a/portalusers/asgi.py +++ b/portalusers/asgi.py @@ -11,6 +11,6 @@ from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'portalusers.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "portalusers.settings") application = get_asgi_application() diff --git a/portalusers/settings.py b/portalusers/settings.py index 828e5b7..2eb42a4 100644 --- a/portalusers/settings.py +++ b/portalusers/settings.py @@ -21,75 +21,75 @@ # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'g6$hm-04*#=aedyrt@1nvig1a0bdzm_gmxfnl0k*+0q0ostl2c' +SECRET_KEY = "g6$hm-04*#=aedyrt@1nvig1a0bdzm_gmxfnl0k*+0q0ostl2c" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = ["*"] -EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -VERSION = '22.06' +VERSION = "22.06" # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', # required for serving swagger ui's css/js files - 'rest_framework', - 'rest_framework_swagger', # required for serving swagger ui - 'corsheaders', - 'core.apps.CoreConfig', - 'drf_yasg', - 'django_rest_passwordreset', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", # required for serving swagger ui's css/js files + "rest_framework", + "rest_framework_swagger", # required for serving swagger ui + "corsheaders", + "core.apps.CoreConfig", + "drf_yasg", + "django_rest_passwordreset", ] # Allow requests from the portal. # Source: https://dzone.com/articles/how-to-fix-django-cors-error MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'corsheaders.middleware.CorsMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "corsheaders.middleware.CorsMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'portalusers.urls' +ROOT_URLCONF = "portalusers.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'portalusers.wsgi.application' +WSGI_APPLICATION = "portalusers.wsgi.application" # Database # https://docs.djangoproject.com/en/3.1/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", } } @@ -99,16 +99,16 @@ AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -116,9 +116,9 @@ # Internationalization # https://docs.djangoproject.com/en/3.1/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True @@ -130,21 +130,18 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.1/howto/static-files/ -STATIC_URL = '/api/static/' +STATIC_URL = "/api/static/" # STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] -STATIC_ROOT = '/var/www/bcoeditor/bco_api/bco_api/static/' - +STATIC_ROOT = "/var/www/bcoeditor/bco_api/bco_api/static/" # Authentication REST_FRAMEWORK = { - 'DEFAULT_PERMISSION_CLASSES': ( - 'rest_framework.permissions.IsAuthenticated', - ), - 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', - 'rest_framework.authentication.SessionAuthentication', - 'rest_framework.authentication.BasicAuthentication', + "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",), + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework_jwt.authentication.JSONWebTokenAuthentication", + "rest_framework.authentication.SessionAuthentication", + "rest_framework.authentication.BasicAuthentication", ), } @@ -152,26 +149,22 @@ # Source: https://dzone.com/articles/how-to-fix-django-cors-error CORS_ORIGIN_ALL_ALL = True CORS_ORIGIN_WHITELIST = ( - 'http://localhost:3000', - 'http://127.0.0.1:3000', - 'http://localhost:8000', - 'http://127.0.0.1:8000' + "http://localhost:3000", + "http://127.0.0.1:3000", + "http://localhost:8000", + "http://127.0.0.1:8000", ) SWAGGER_SETTINGS = { - 'SECURITY_DEFINITIONS': { - 'basic': { - 'type': 'basic' - } - }, - "DEEP_LINKING": True + "SECURITY_DEFINITIONS": {"basic": {"type": "basic"}}, + "DEEP_LINKING": True, } JWT_AUTH = { - 'JWT_RESPONSE_PAYLOAD_HANDLER': 'portalusers.utils.my_jwt_response_handler', - 'JWT_EXPIRATION_DELTA': timedelta(seconds=604800), - 'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=14), - 'JWT_ALLOW_REFRESH': True, + "JWT_RESPONSE_PAYLOAD_HANDLER": "portalusers.utils.my_jwt_response_handler", + "JWT_EXPIRATION_DELTA": timedelta(seconds=604800), + "JWT_REFRESH_EXPIRATION_DELTA": timedelta(days=14), + "JWT_ALLOW_REFRESH": True, } # LOGGING diff --git a/portalusers/urls.py b/portalusers/urls.py index eef0b91..676e1bc 100644 --- a/portalusers/urls.py +++ b/portalusers/urls.py @@ -38,12 +38,21 @@ # ends here urlpatterns = [ - re_path(r'^users/doc(?P\.json|\.yaml)$', - schema_view.without_ui(cache_timeout=0), name='schema-json'), #Here - path('users/docs/', schema_view.with_ui('swagger', cache_timeout=0), - name='schema-swagger-ui'), # Here - path('users/redoc/', schema_view.with_ui('redoc', cache_timeout=0), - name='schema-redoc'), # Here - path('users/admin/', admin.site.urls), - path('', include('core.urls')) + re_path( + r"^users/doc(?P\.json|\.yaml)$", + schema_view.without_ui(cache_timeout=0), + name="schema-json", + ), # Here + path( + "users/docs/", + schema_view.with_ui("swagger", cache_timeout=0), + name="schema-swagger-ui", + ), # Here + path( + "users/redoc/", + schema_view.with_ui("redoc", cache_timeout=0), + name="schema-redoc", + ), # Here + path("users/admin/", admin.site.urls), + path("", include("core.urls")), ] diff --git a/portalusers/utils.py b/portalusers/utils.py index 52b8041..50e8b73 100644 --- a/portalusers/utils.py +++ b/portalusers/utils.py @@ -10,31 +10,33 @@ from core.serializers import UserSerializer from core.models import ApiInfo + def update_api_info(api): """Update API info""" - url = api['public_hostname']+'/api/accounts/describe/' - token = api['token'] - header = {'Authorization': f'Token {token}'} + url = api["public_hostname"] + "/api/accounts/describe/" + token = api["token"] + header = {"Authorization": f"Token {token}"} try: response = requests.post(url, headers=header) except requests.exceptions.ConnectionError: - return Response(status=status.HTTP_503_SERVICE_UNAVAILABLE, - data='ConnectionError' + return Response( + status=status.HTTP_503_SERVICE_UNAVAILABLE, data="ConnectionError" ) if response.status_code == 401: - return Response(status=status.HTTP_401_UNAUTHORIZED, - data=json.loads(response.text) + return Response( + status=status.HTTP_401_UNAUTHORIZED, data=json.loads(response.text) ) if response.status_code is 400: - return Response(status=status.HTTP_400_BAD_REQUEST, - data=json.loads(response.text) + return Response( + status=status.HTTP_400_BAD_REQUEST, data=json.loads(response.text) ) return Response(status=status.HTTP_200_OK, data=json.loads(response.text)) + def my_jwt_response_handler(token, user=None, request=None): """JWT @@ -43,17 +45,19 @@ def my_jwt_response_handler(token, user=None, request=None): TODO: refer to API code for a cleaner way to do this. """ - user_info = UserSerializer(user, context={'request': request}).data - for api in user_info['apiinfo']: - api_object = ApiInfo.objects.get(token=api['token']) + user_info = UserSerializer(user, context={"request": request}).data + for api in user_info["apiinfo"]: + api_object = ApiInfo.objects.get(token=api["token"]) api_update = update_api_info(api) if api_update.status_code == 200: - api_object.other_info = api_update.data['other_info'] - api_object.other_info['last_update'] = f"{datetime.datetime.utcnow().isoformat()[:-2]}Z" - api_object.other_info['status'] = api_update.status_code + api_object.other_info = api_update.data["other_info"] + api_object.other_info[ + "last_update" + ] = f"{datetime.datetime.utcnow().isoformat()[:-2]}Z" + api_object.other_info["status"] = api_update.status_code api_object.save() # print(api_object.other_info) - user_info['groups'] = [list(i.items())[0][1] for i in user_info['groups']] + user_info["groups"] = [list(i.items())[0][1] for i in user_info["groups"]] - return {'token': token, 'user': user_info} + return {"token": token, "user": user_info} diff --git a/portalusers/wsgi.py b/portalusers/wsgi.py index 067fff4..4792886 100644 --- a/portalusers/wsgi.py +++ b/portalusers/wsgi.py @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'portalusers.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "portalusers.settings") application = get_wsgi_application() diff --git a/simple_api_info_fill.py b/simple_api_info_fill.py index 4c55a4c..5f1efc0 100644 --- a/simple_api_info_fill.py +++ b/simple_api_info_fill.py @@ -1,15 +1,21 @@ import django from django.contrib.auth.models import User -user = User.objects.get(username = 'carmstrong1') + +user = User.objects.get(username="carmstrong1") from core.models import ApiInfo -test = ApiInfo(username = user, hostname = '127.0.0.1:8000', human_readable = 'BCO API (Generic)', apikey = '@#48&8fdsnj4012nDfh4jkl2') +test = ApiInfo( + username=user, + hostname="127.0.0.1:8000", + human_readable="BCO API (Generic)", + apikey="@#48&8fdsnj4012nDfh4jkl2", +) test.save() -#suppress account +# suppress account -#API Key needs to give list of tables -> gives list of prefixes (parsed) +# API Key needs to give list of tables -> gives list of prefixes (parsed) # -> Need keys/tables model # -> server request for tables sends back comma-separated list