diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 6a761a0..4df92e9 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -1,4 +1,4 @@ -<<<<<<< HEAD + name: Django Tests on: push: @@ -21,7 +21,7 @@ jobs: python-version:3.6 -name: Install dependencies run: | - python -m pip install --upgrade pip + python3 -m pip install --upgrade pip pip3 install -r requirements.txt -name: Lint with flake8 run: | @@ -35,41 +35,3 @@ jobs: -name: Django Testing run : | python3 manage.py test -======= -# This is a basic workflow to help you get started with Actions - -name: CI - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the master branch - push: - branches: [ master ] - pull_request: - branches: [ master ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - # Runs a single command using the runners shell - - name: Run a one-line script - run: echo Hello, world! - - # Runs a set of commands using the runners shell - - name: Run a multi-line script - run: | - echo Add other actions to build, - echo test, and deploy your project. ->>>>>>> f0b084f597c2d73179d049e46e029f826e8d6147 diff --git a/.github/workflows/testing_one.yml b/.github/workflows/testing_one.yml index a048f35..ed4652c 100644 --- a/.github/workflows/testing_one.yml +++ b/.github/workflows/testing_one.yml @@ -17,6 +17,3 @@ jobs: run: | python3 -m pip install --upgrade pip pip3 install pylint - pip3 install -r requirements.txt - python3 manage.py test - diff --git a/jangoadmin/api/__init__.py b/jangoadmin/api/__init__.py index e69de29..77224f4 100644 --- a/jangoadmin/api/__init__.py +++ b/jangoadmin/api/__init__.py @@ -0,0 +1 @@ +default_app_config="api.ApiConfig" \ No newline at end of file diff --git a/jangoadmin/api/apps.py b/jangoadmin/api/apps.py index d87006d..310bd19 100644 --- a/jangoadmin/api/apps.py +++ b/jangoadmin/api/apps.py @@ -1,5 +1,5 @@ from django.apps import AppConfig - - class ApiConfig(AppConfig): name = 'api' + def ready(self): + from api import signals diff --git a/jangoadmin/api/enums.py b/jangoadmin/api/enums.py new file mode 100644 index 0000000..ad2e8c4 --- /dev/null +++ b/jangoadmin/api/enums.py @@ -0,0 +1,8 @@ +from enum import Enum +class TokenType(Enum): + Reset_Password="Reset Password" + Forget_Password="Forget Password" + Email_Registration_Confirmation="Email Registration Confirmation" + + + diff --git a/jangoadmin/api/migrations/0002_token.py b/jangoadmin/api/migrations/0002_token.py new file mode 100644 index 0000000..60cf6a6 --- /dev/null +++ b/jangoadmin/api/migrations/0002_token.py @@ -0,0 +1,27 @@ +# Generated by Django 3.0.14 on 2021-04-22 06:18 + +import api.enums +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Token', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('token_type', models.CharField(choices=[(api.enums.TokenType['Reset_Password'], 'Reset Password'), (api.enums.TokenType['Forget_Password'], 'Forget Password'), (api.enums.TokenType['Email_Registration_Confirmation'], 'Email Registration Confirmation')], max_length=100, verbose_name='Token Type')), + ('token', models.CharField(blank=True, default=None, max_length=100, null=True, verbose_name='Token')), + ('created_at', models.DateTimeField(blank=True, default=None, null=True, verbose_name='Created At')), + ('expiry_minutes', models.IntegerField(default=30, verbose_name='Expiry Minutes')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/jangoadmin/api/migrations/0003_user_avatar.py b/jangoadmin/api/migrations/0003_user_avatar.py new file mode 100644 index 0000000..3d2bcb8 --- /dev/null +++ b/jangoadmin/api/migrations/0003_user_avatar.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.14 on 2021-04-23 07:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0002_token'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='avatar', + field=models.FileField(blank=True, default=None, null=True, upload_to='avatars', verbose_name='Avatar'), + ), + ] diff --git a/jangoadmin/api/models.py b/jangoadmin/api/models.py index 1a165ed..2c44b1d 100644 --- a/jangoadmin/api/models.py +++ b/jangoadmin/api/models.py @@ -1,6 +1,7 @@ from django.db import models from django.contrib.auth.models import AbstractBaseUser,PermissionsMixin from django.contrib.auth.base_user import BaseUserManager +from .enums import TokenType class UserManager(BaseUserManager): def create_superuser(self,email,username,password): ''' @@ -29,6 +30,7 @@ class User(AbstractBaseUser,PermissionsMixin): created_on=models.DateTimeField("Created On",auto_now_add=True) updated_on=models.DateTimeField("Updated On",auto_now_add=True) email=models.EmailField("Email",null=False,blank=False,unique=True,error_messages={"unique":"OOPS,An account with this email is already regisgtered"}) + avatar=models.FileField("Avatar",null=True,blank=True,upload_to="avatars",default=None) username=models.CharField("UserName",null=False,blank=False,max_length=100,unique=True,error_messages={"unique":"An UserName with this username is already regisgtered"}) objects=UserManager() REQUIRED_FIELDS=['username'] @@ -36,4 +38,12 @@ class User(AbstractBaseUser,PermissionsMixin): def save(self,*args,**kwargs): self.email=self.email.lower() self.username=self.username.lower() - super(User,self).save(*args,**kwargs) \ No newline at end of file + super(User,self).save(*args,**kwargs) +class Token(models.Model): + token_type=models.CharField("Token Type",max_length=100,choices=((type,type.value) for type in TokenType)) + token=models.CharField("Token",max_length=100,null=True,blank=True,default=None) + user=models.ForeignKey(User,on_delete=models.CASCADE) + created_at=models.DateTimeField("Created At",null=True,blank=True,default=None) + expiry_minutes=models.IntegerField("Expiry Minutes",default=30) + def __str__(self): + return str(self.token) + "_" + str(self.token) \ No newline at end of file diff --git a/jangoadmin/api/signals.py b/jangoadmin/api/signals.py new file mode 100644 index 0000000..c719935 --- /dev/null +++ b/jangoadmin/api/signals.py @@ -0,0 +1,3 @@ +import logging,traceback +from api.models import User +logger=logging.getLogger(__name__) \ No newline at end of file diff --git a/jangoadmin/api/test.py b/jangoadmin/api/test.py index 6b79cf9..13f5512 100644 --- a/jangoadmin/api/test.py +++ b/jangoadmin/api/test.py @@ -21,3 +21,8 @@ def test_login_without_data(self): my_dict=ast.literal_eval(self.data) response=self.client.post(reverse("login"),data=my_dict,format="json") self.assertEqual(response.status_code,status.HTTP_400_BAD_REQUEST) + def test_change_password_with_data(self): + self.data='{"new_password":"rahul123","confirm_password":"rahul123","old_password":"test@123"}' + my_dict=ast.literal_eval(self.data) + response=self.client.post(reverse("ChangePassWord"),data=my_dict,format="json") + self.assertEqual(response.status_code,status.HTTP_401_UNAUTHORIZED) diff --git a/jangoadmin/api/urls.py b/jangoadmin/api/urls.py index 3acb337..92194ab 100644 --- a/jangoadmin/api/urls.py +++ b/jangoadmin/api/urls.py @@ -1,14 +1,14 @@ from django.conf.urls import url from django.urls import path from rest_framework.urlpatterns import format_suffix_patterns -from api.views import UserSignUp,LoginView - +from api.views import UserSignUp,LoginView,ChangePassWord,AvatarView urlpatterns = [ path('register/', UserSignUp.as_view(),name="register"), path('login/', LoginView.as_view(),name="login"), + path('change-password/', ChangePassWord.as_view(),name="ChangePassWord"), # path('profile/', MyProfile), - # path('avatar/', Avatar), - # path('change-password/', ChangePassword), + path('avatar/', AvatarView.as_view(),name="AvatarView"), + # path('forgot-password/', ForgotPassword), # path('logout/', Logout), # path('home/', Dashboard), diff --git a/jangoadmin/api/views.py b/jangoadmin/api/views.py index 85f696b..fa8daa0 100644 --- a/jangoadmin/api/views.py +++ b/jangoadmin/api/views.py @@ -316,7 +316,10 @@ # data = {"status":200,"data":{"message":1}} # return JsonResponse(data) -import logging,traceback +import json,logging,traceback,os,boto3 +from decouple import config +from boto3.s3.transfer import S3Transfer +from django.core.files.storage import FileSystemStorage from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status @@ -324,7 +327,14 @@ from .serializers import UserSerializer from django.contrib.auth import authenticate,login from rest_framework.authtoken.models import Token +from rest_framework.permissions import IsAuthenticated +from rest_framework.authentication import TokenAuthentication logger=logging.getLogger(__name__) +def checkAuth(request): + if Token.objects.filter(key=request.META.get('HTTP_TOKEN')): + return Token.objects.filter(key=request.META.get('HTTP_TOKEN'))[0] + else: + return 0 class UserSignUp(APIView): ''' API FOR SIGNUP @@ -392,4 +402,89 @@ def post(self,request): except Exception: logger.exception(traceback.format_exc()) logger.exception("Something went wrong in " + "Post" + "login") - return Response({"status":True,"message":"Something went wrong"},status=status.HTTP_500_INTERNAL_SERVER_ERROR) \ No newline at end of file + return Response({"status":True,"message":"Something went wrong"},status=status.HTTP_500_INTERNAL_SERVER_ERROR) +class ChangePassWord(APIView): + ''' + API FOR CHANGING PASSWORD + YOU SHOULD BE AUTHENICATED USER IN ORDER TO HIT THIS API + + ''' + authentication_classes = (TokenAuthentication,) + permission_classes=(IsAuthenticated,) + def post(self,request): + try: + params=request.data + + try: + current_password=params.pop("new_password") + except Exception: + current_password=None + if not current_password: + return Response({"status":False, "message":"OOPS, Please Mention New Password"},status=status.HTTP_400_BAD_REQUEST) + try: + confirm_password=params.pop("confirm_password") + except Exception: + current_password=None + if not confirm_password: + return Response({"status":False,"message":"OOPS,Please Confirm your password Once"},status=status.HTTP_400_BAD_REQUEST) + if current_password!=confirm_password: + return Response({"status":False,"message":"OOPS,Passowrd didn't matched"},status=status.HTTP_400_BAD_REQUEST) + try: + old_password=params.pop("old_password") + except Exception: + old_password=None + if not old_password: + return Response({"status":False,'message':"Old Password is required"},status=status.HTTP_400_BAD_REQUEST) + email=request.user.email + user=authenticate(email=email,password=old_password) + if not user: + return Response({"status":False,"message":"Credentials are invalid"},status=status.HTTP_400_BAD_REQUEST) + user=User.objects.get(email=email) + user.set_password(current_password) + user.save() + return Response({"status":True,"message":"Password Updated Successfully"},status=status.HTTP_200_OK) + except Exception: + logger.exception(traceback.format_exc()) + logger.exception("Something went wrong in " + "POST" + "changepassword") + return Response({"status":False,"message":"OOPS,Something went wrong"},status=status.HTTP_500_INTERNAL_SERVER_ERROR) +class AvatarView(APIView): + def get(self,request): + try: + token=checkAuth(request) + if token== 0: + data={"status":403,"data":{"message":"Not logged in"}} + return Response(data) + AWS_STORAGE_URL=config("AWS_STORAGE_URL",default="") + user=token.user + if str(token.user.avatar)!="": + data={"status":200,"data":{"url":AWS_STORAGE_URL+str(token.user.profile.avatar)}} + else: + data={"status":404,"data":{"message":"avatar missing"}} + return Response(data) + except Exception: + logger.exception(traceback.format_exc()) + logger.exception("Something went wrong in" + "GET" + "AvatarView") + return Response({"status":False,"message":"Something went wrong"},status=status.HTTP_500_INTERNAL_SERVER_ERROR) + def post(self,request): + try: + avatar = request.FILES['file'] + AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID', default='') + AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY', default='') + AWS_STORAGE_BUCKET_RGN = config('AWS_STORAGE_BUCKET_RGN', default='us-east-1') + AWS_STORAGE_BUCKET_NAME = config('AWS_STORAGE_BUCKET_NAME', default='test') + fs = FileSystemStorage(os.getcwd()+'/static/img/avatars') + fname = avatar.name + filename = fs.save(fname,avatar) + s3_path = 'avatars/'+fname + local_path = os.getcwd()+'/static/img/avatars/'+fname + transfer = S3Transfer(boto3.client('s3', AWS_STORAGE_BUCKET_RGN, aws_access_key_id = AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY,use_ssl=False)) + client = boto3.client('s3') + transfer.upload_file(local_path, AWS_STORAGE_BUCKET_NAME, s3_path,extra_args={'ACL': 'public-read'}) + User.objects.filter(user=token.user.id).update(avatar=s3_path) + os.remove(local_path) + token = checkAuth(request) + user = token.user + except Exception as error: + logger.exception(traceback.format_exc()) + logger.exception("Something went wrong " + 'GET' + 'AvatarView') + return Response({"status":False,"message":"Something went wrong","error":error},status=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/jangoadmin/jangoadmin/settings.py b/jangoadmin/jangoadmin/settings.py index 3db5510..6684019 100644 --- a/jangoadmin/jangoadmin/settings.py +++ b/jangoadmin/jangoadmin/settings.py @@ -90,13 +90,11 @@ } REST_FRAMEWORK = { - 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework.authentication.TokenAuthentication', - ), - 'DEFAULT_RENDERER_CLASSES': ( - 'rest_framework.renderers.JSONRenderer', - ), - 'DEFAULT_PARSER_CLASSES': ( + + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.TokenAuthentication', + ), + 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.MultiPartParser', 'rest_framework.parsers.FileUploadParser', @@ -129,6 +127,7 @@ } } + # Password validation # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators diff --git a/jangoadmin/jangoadmin/urls.py b/jangoadmin/jangoadmin/urls.py index 844a3db..f4dae65 100644 --- a/jangoadmin/jangoadmin/urls.py +++ b/jangoadmin/jangoadmin/urls.py @@ -28,7 +28,7 @@ #router.register(r'bread', BreadViewSet), urlpatterns = [ - #path('admin/', admin.site.urls), + path('admin/', admin.site.urls), #path('', include(router.urls)), #path('secret/', include('secret.urls')), path('api/', include('api.urls')), diff --git a/jangoadmin/pytest.ini.save b/jangoadmin/pytest.ini.save new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/jangoadmin/pytest.ini.save @@ -0,0 +1 @@ + diff --git a/jangoadmin/requirements.txt b/jangoadmin/requirements.txt index 51f2452..bea4da0 100644 --- a/jangoadmin/requirements.txt +++ b/jangoadmin/requirements.txt @@ -1,5 +1,6 @@ appdirs==1.4.3 asgiref==3.2.7 +attrs==20.3.0 boto3==1.13.15 botocore==1.16.15 CacheControl==0.12.6 @@ -7,6 +8,7 @@ certifi==2020.4.5.1 chardet==3.0.4 colorama==0.4.3 contextlib2==0.6.0 +coverage==5.5 distlib==0.3.0 distro==1.4.0 Django==3.0.14 @@ -14,18 +16,29 @@ django-kuldeep==1.8 django-storages==1.9.1 djangorestframework==3.11.2 docutils==0.15.2 +fhir==0.0.4 +flake8==3.9.0 html5lib==1.0.1 idna==2.8 +importlib-metadata==3.10.0 +iniconfig==1.1.1 ipaddr==2.2.0 jmespath==0.10.0 +kcalculator==0.0.10 lockfile==0.12.2 +mccabe==0.6.1 msgpack==0.6.2 -kcalculator==0.0.10 mysqlclient==1.4.6 packaging==20.3 pep517==0.8.2 +pkg-resources==0.0.0 +pluggy==0.13.1 progress==1.5 +py==1.10.0 +pycodestyle==2.7.0 +pyflakes==2.3.1 pyparsing==2.4.6 +pytest==6.2.3 python-dateutil==2.8.1 python-decouple==3.3 pytoml==0.1.21 @@ -36,5 +49,9 @@ s3transfer==0.3.3 schedule==0.6.0 six==1.15.0 sqlparse==0.3.1 +standalone==1.0.1 +toml==0.10.2 +typing-extensions==3.7.4.3 urllib3==1.25.9 webencodings==0.5.1 +zipp==3.4.1 diff --git a/jangoadmin/static/img/avatars/image_1DFbfp6.png b/jangoadmin/static/img/avatars/image_1DFbfp6.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_1DFbfp6.png differ diff --git a/jangoadmin/static/img/avatars/image_3f6ES3v.png b/jangoadmin/static/img/avatars/image_3f6ES3v.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_3f6ES3v.png differ diff --git a/jangoadmin/static/img/avatars/image_5JtovMd.png b/jangoadmin/static/img/avatars/image_5JtovMd.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_5JtovMd.png differ diff --git a/jangoadmin/static/img/avatars/image_9QuiUb1.png b/jangoadmin/static/img/avatars/image_9QuiUb1.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_9QuiUb1.png differ diff --git a/jangoadmin/static/img/avatars/image_C0vILff.png b/jangoadmin/static/img/avatars/image_C0vILff.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_C0vILff.png differ diff --git a/jangoadmin/static/img/avatars/image_M01KKrr.png b/jangoadmin/static/img/avatars/image_M01KKrr.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_M01KKrr.png differ diff --git a/jangoadmin/static/img/avatars/image_OEJbY0c.png b/jangoadmin/static/img/avatars/image_OEJbY0c.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_OEJbY0c.png differ diff --git a/jangoadmin/static/img/avatars/image_OEdlDN7.png b/jangoadmin/static/img/avatars/image_OEdlDN7.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_OEdlDN7.png differ diff --git a/jangoadmin/static/img/avatars/image_PLLXM1K.png b/jangoadmin/static/img/avatars/image_PLLXM1K.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_PLLXM1K.png differ diff --git a/jangoadmin/static/img/avatars/image_WcZGYHK.png b/jangoadmin/static/img/avatars/image_WcZGYHK.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_WcZGYHK.png differ diff --git a/jangoadmin/static/img/avatars/image_bdZBbRJ.png b/jangoadmin/static/img/avatars/image_bdZBbRJ.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_bdZBbRJ.png differ diff --git a/jangoadmin/static/img/avatars/image_hRNG4U0.png b/jangoadmin/static/img/avatars/image_hRNG4U0.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_hRNG4U0.png differ diff --git a/jangoadmin/static/img/avatars/image_hfGXtEh.png b/jangoadmin/static/img/avatars/image_hfGXtEh.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_hfGXtEh.png differ diff --git a/jangoadmin/static/img/avatars/image_kPf6heG.png b/jangoadmin/static/img/avatars/image_kPf6heG.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_kPf6heG.png differ diff --git a/jangoadmin/static/img/avatars/image_riKGncY.png b/jangoadmin/static/img/avatars/image_riKGncY.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_riKGncY.png differ diff --git a/jangoadmin/static/img/avatars/image_xv1oeRs.png b/jangoadmin/static/img/avatars/image_xv1oeRs.png new file mode 100644 index 0000000..f766ebc Binary files /dev/null and b/jangoadmin/static/img/avatars/image_xv1oeRs.png differ diff --git a/jangoadmin/testing.yml b/jangoadmin/testing.yml new file mode 100644 index 0000000..e69de29 diff --git a/pytest.ini b/pytest.ini index 7c8d4c4..8cb7cb9 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,2 @@ -import settings -[pytest] -DJANGO_SETTINGS_MODULE=settings +