get() returned more than one AccessToken - python

I am facing 2 issues here.
1) If user has not old token, a new one should be generated and logged in but it is not happening so.
2)A newly created user when logs in gets 'You have multiple authentication backends configured and therefore must provide the backend argument or set the backend attribute on the user.'
3)old user when trying to login gets an error of get() returned more than one AccessToken -- it returned 8!
Expert might understand from my code
class UserLoginAPI(APIView):
permission_classes = [AllowAny]
serializer_class = UserLoginSerializer
def post(self, request, *args, **kwargs):
access_token = request.GET.get('access_token')
data = request.data
print('data',data)
serializer = UserLoginSerializer(data=data)
if serializer.is_valid(raise_exception=True):
new_data = serializer.data
if new_data:
app = Application.objects.get(name="Foodie")
try:
user = User.objects.get(username=data['username'])
print ('user',user)
except ObjectDoesNotExist:
return HttpResponse("Can't find this user")
else:
try:
access_token = AccessToken.objects.get(user=user)
except ObjectDoesNotExist:
return HttpResponse('Have not set any token')
else:
access_token.delete()
new_token = generate_token()
print('new_token',new_token)
AccessToken.objects.create(user=user, application=app, expires=datetime.now() + timedelta(days=365),token=new_token)
print('aceess',AccessToken)
login(request, user)
return Response(new_data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
class UserLoginSerializer(ModelSerializer):
username = CharField()
class Meta:
model = User
fields = ['username', 'password']
class UserCreateSerializer(ModelSerializer):
class Meta:
model = User
extra_kwargs = {"password": {"write_only": True}}
def create(self, validated_data):
user_obj = User(
username = username,
first_name = first_name,
last_name = last_name,
email = email
)
user_obj.set_password(password)
user_obj.save()
if user_obj:
expire_seconds = oauth2_settings.user_settings['ACCESS_TOKEN_EXPIRE_SECONDS']
scopes = oauth2_settings.user_settings['SCOPES']
application = Application.objects.get(name="Foodie")
expires = datetime.now() + timedelta(seconds=expire_seconds)
access_token = AccessToken.objects.create(user=user_obj,
application=application,
token = generate_token(),
expires=expires,
scope=scopes)
return validated_data

Related

DjangoRestFramework: Unit Testing AssertionError: 404 != 200 while Test update

I've started writing unit tests for my API's but am stuck while updating user details received an assertion error of 404!=200. I'm sharing mycode for the reference.
Please do let me know my mistake and a brief explanation as I'm new to django rest.
I've created only one class "class TestUser(APITestCase):" for testing 2 API is it fine as I'm extending the URL in the update client patch?
models.py
mport email
from pyexpat import model
from django.db import models
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser)
GENDER_CHOICES = (
(0, 'male'),
(1, 'female'),
(2, 'not specified'),)
class UserManager(BaseUserManager):
def create_user(self, email, name,contact_number,gender,address,state,city,country,pincode,dob ,password=None, password2=None):
"""
Creates and saves a User with the given email, name and password.
"""
if not email:
raise ValueError('User must have an email address')
user = self.model(
email=self.normalize_email(email),
name=name,
contact_number=contact_number,
gender=gender,
address=address,
state=state,
city=city,
country=country,
pincode=pincode,
dob=dob,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, name,contact_number,gender,address,state,city,country,pincode,dob , password=None):
"""
Creates and saves a superuser with the given email, name and password.
"""
user = self.create_user(
email,
name=name,
contact_number=contact_number,
gender=gender,
address=address,
state=state,
city=city,
country=country,
pincode=pincode,
dob=dob,
password=password,
)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
email = models.EmailField(verbose_name='Email',max_length=255,unique=True)
name = models.CharField(max_length=200)
contact_number= models.IntegerField()
gender = models.IntegerField(choices=GENDER_CHOICES)
address= models.CharField(max_length=100)
state=models.CharField(max_length=100)
city=models.CharField(max_length=100)
country=models.CharField(max_length=100)
pincode= models.IntegerField()
dob = models.DateField(null= True)
# is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name','contact_number','gender','address','state','city','country','pincode','dob']
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
#property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
views.py
class UserListCreateAPIView(generics.ListCreateAPIView):
permission_classes = [IsUserOrIsAdmin]
serializer_class = UserSerializer
def get_queryset(self):
"""
This view should return a list of all the purchases
for the currently authenticated user.
"""
if self.request.user.is_staff == False:
user_data= self.request.user.id
data = User.objects.filter(id= user_data)
return data
else:
data = User.objects.all()
return data
class UserRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsUserOrIsAdmin]
serializer_class = UserSerializer
def get_queryset(self):
"""
This view should return a list of all the purchases
for the currently authenticated user.
"""
if self.request.user.is_staff == False:
user_data= self.request.user.id
data = User.objects.filter(id= user_data)
return data
else:
data = User.objects.all()
return data
urls.py
path('customer_info/', UserListCreateAPIView.as_view()),
path('customer_info/<int:pk>/', UserRetrtieveUpdateDestroyAPIView.as_view()),
tests.py
from rest_framework.test import APITestCase
from .models import User
class TestUser(APITestCase):
url = "/api/v1/customer_info/"
def setUp(self):
User.objects.create(email="naveen#example.com",name="Naveen",contact_number="9090909098",gender="0",address="jaipur",state="Rajasthan",city="Jaipur",country="India",pincode="302016",dob="1907-07-20",is_active="True",
is_admin= "False")
def test_get_User(self):
response = self.client.get(self.url)
print(response)
result = response.json()
# print(result)
self.assertEqual(response.status_code, 200)
self.assertIsInstance(result, list)
def test_post_User(self):
# definition
data = {
"email":"ujjval#example.com",
"password":"123456",
"name":"ujjval",
"contact_number":"9090909098",
"gender":"0",
"address":"jaipur",
"state":"Rajasthan",
"city":"Jaipur",
"country":"India",
"pincode":"302016",
"dob":"1907-07-20"
}
# process
response = self.client.post(self.url, data=data)
# print(response)
result = response.json()
# print(result)
# assert
self.assertEqual(response.status_code, 201)
self.assertEqual(result["email"], "ujjval#example.com")
def test_update_User(self):
pk = "1"
data = {
"email":"naveen#example.com",
}
response = self.client.patch(self.url + f"{pk}/", data=data)
result = response.json()
print(result)
self.assertEqual(response.status_code, 200)
self.assertEqual(result["email"], "ujjval#example.com")
I think you need to set the names of the URL paths and use them in the test class.
path('customer_info/', UserListCreateAPIView.as_view(), name="customer_info_view"),
path('customer_info/<int:pk>/', UserRetrtieveUpdateDestroyAPIView.as_view(), name="customer_info_detail_view"),
And in the test class,
from django.urls import reverse
class TestUser(APITestCase):
def setUp(self):
self.user = User.objects.create(email="naveen#example.com",name="Naveen",contact_number="9090909098",gender="0",address="jaipur",state="Rajasthan",city="Jaipur",country="India",pincode="302016",dob="1907-07-20",is_active="True", is_admin= "False")
def test_get_User(self):
response = self.client.get(reverse('customer_info_view'))
...
def test_post_User(self):
data = {
...
}
response = self.client.post(reverse('customer_info_view'), data=data)
...
def test_update_user(self):
pk = self.user.id
data = {
"email":"naveen#example.com",
}
response = self.client.patch(reverse('customer_info_detail_view', kwargs={
'pk': pk,
}), data, content_type='application/json')
result = response.json()
self.assertEqual(response.status_code, 200)
# self.assertEqual(result["email"], "ujjval#example.com")

How to solve error 401 unauthorized login in DRF simple jwt user login

I am creating DRF authentication APIs for Abstract Base users in my Django project and using simple JWT. The registration and email verification APIs work fine, but when I try to log in using the credentials of a valid user, I get an error 401 unauthorized access.
the custom user model in models.py
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=255, unique=True,db_index=True)
email = models.EmailField(max_length=255, unique=True,db_index=True)
is_verified = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELD = ['username']
objects = UserManager()
def __str__(self):
return self.email
def tokens(self):
refresh = RefreshToken.for_user(self)
return{
'refresh':str(refresh),
'access': str(refresh.access_token)
}
Here are my login and verification views:
class VerifyEmail(views.APIView):
serializer_class = EmailVerificationSerializer
token_param_config = openapi.Parameter('token',in_=openapi.IN_QUERY, description='Description', type=openapi.TYPE_STRING)
#swagger_auto_schema(manual_parameters=[token_param_config])
def get(self, request):
token = request.GET.get('token')
try:
payload = jwt.decode(token,settings.SECRET_KEY, algorithms=['HS256'])
user = User.objects.get(id=payload['user_id'])
if not user.is_verified:
user.is_verified = True
user.save()
return Response({'email': 'Succesfully activated'}, status = status.HTTP_200_OK)
except jwt.ExpiredSignatureError as identifier:
return Response({'error': 'Activation Expired'}, status= status.HTTP_400_BAD_REQUEST)
except jwt.exceptions.DecodeError as identifier:
return Response({'error': 'Invalid token'}, status=status.HTTP_400_BAD_REQUEST)
class LoginAPIView(generics.GenericAPIView):
serializer_class = LoginSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=status.HTTP_200_OK)
Here is my login serializer:
class LoginSerializer(serializers.ModelSerializer):
email = serializers.EmailField(max_length=255, min_length=3)
password = serializers.CharField(max_length=68, min_length=8, write_only=True)
username = serializers.CharField(max_length=255, min_length=3, read_only = True)
tokens = serializers.CharField(max_length=68, min_length=8, read_only = True)
class Meta:
model = User
fields = ['email', 'password', 'username', 'tokens']
def validate(self, attrs):
email = attrs.get('email', '')
password = attrs.get('password', '')
user = auth.authenticate(email=email, password=password)
if not user:
raise AuthenticationFailed('Invalid Credentials, try again!')
if not user.is_active:
raise AuthenticationFailed('Acccount disabled, please contact admin')
if not user.is_verified:
raise AuthenticationFailed('Email is not verified')
return {
'email': user.email,
'username': user.username,
'tokens': user.tokens
}
return super().validate(attrs)
So the error raised is "Invalid credentials" meaning that details of the user don't exist, while the users are actually there when I check the database.
Anyone, please help.
So after a lot of googling and headaches, I ended up reading the Simple Jwt documentation again and as it turns out, the 401 error occurs if the user is not active. In my models.py above, by default, my user's is_verified and is_active are False. The VerifyEmail view changed the is_verified to True after the users verified their emails but their is_active remained False hence giving the error 401.
My solution was adding an is_active=True for when the user verifies their email:
Here is the VerifyEmail view in views.py
class VerifyEmail(views.APIView):
serializer_class = EmailVerificationSerializer
token_param_config = openapi.Parameter('token',in_=openapi.IN_QUERY, description='Description', type=openapi.TYPE_STRING)
#swagger_auto_schema(manual_parameters=[token_param_config])
def get(self, request):
token = request.GET.get('token')
try:
payload = jwt.decode(token,settings.SECRET_KEY, algorithms=['HS256'])
user = User.objects.get(id=payload['user_id'])
if not user.is_verified:
user.is_verified = True
user.is_active = True # New
user.save()
return Response({'email': 'Succesfully activated'}, status = status.HTTP_200_OK)
except jwt.ExpiredSignatureError as identifier:
return Response({'error': 'Activation Expired'}, status= status.HTTP_400_BAD_REQUEST)
except jwt.exceptions.DecodeError as identifier:
return Response({'error': 'Invalid token'}, status=status.HTTP_400_BAD_REQUEST)
This worked for me.
What you want is to exclude your login view from the project-wide authentication check that you have added in settings.py.
class LoginAPIView(generics.GenericAPIView):
permission_classes = ()
authentication_classes = ()
serializer_class = LoginSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=status.HTTP_200_OK)

How to write a login view for my register view in rest frame work?

I'm a beginner in Django and the rest framework and I'm trying to write a class-based login view with the rest framework for my register view please help me for writing a login class-based view what is important is view be class-based with rest
this is a registered view of my project and then its serializer at the bottom of that
class RegisterView(GenericAPIView):
serializer_class = UserSerializer
permission_classes = (permissions.AllowAny,)
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
user_data = serializer.data
user = User.objects.get(email=user_data['email'])
token = RefreshToken.for_user(user).access_token
current_site = get_current_site(request).domain
print(current_site)
# relativeLink = reverse('verify-email')
# print(type(relativeLink))
absurl = 'http://' + current_site + "?token=" + str(token)
email_body = 'سلام' + user.username + '\nبرای فعال سازی حساب خود وارد لینک زیر شوید' + '\n' \
+ absurl
data = {'email_body': email_body, 'to_email': user.email, 'email_subject': 'Verify your email'}
Util.send_email(data)
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
it is a register view serializer in serializer.py
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(
max_length=65, min_length=8, write_only=True)
confirm_password = serializers.CharField(
max_length=65, min_length=8, write_only=True)
def validate_email(self, value):
lower_email = value.lower()
if User.objects.filter(email__iexact=lower_email).exists():
raise serializers.ValidationError("ایمیل تکراری است")
return lower_email
def validate(self, data):
if not data.get('password') or not data.get('confirm_password'):
raise serializers.ValidationError("لطفا پسورد را وارد و تایید کنید ")
if data.get('password') != data.get('confirm_password'):
raise serializers.ValidationError("پسورد اشتباه است")
return data
class Meta:
model = User
fields = ['username', 'email', 'password', 'confirm_password'
]
write_only_fields = ('password', 'repeat_password')
def create(self, validated_data):
user = User.objects.create(
username=validated_data['username'],
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user.set_password(validated_data['confirm_password'])
user.save()
return user
class LoginView(APIView):
def post(self, request):
username = request.data['username']
password = request.data['password']
# use a method to get access token (from the package you are using)
# access token class will return access token if the user is authenticated
# otherwise it will return error response
pass

How to get user object from request in django rest framework?

I want to get the password of curent user from request body and verify it.
I am trying to get user object from request like this:
class UserPasswordUpdateAsAdminViewSet(APIView):
def patch(self, request, pk):
serializer = ResetPasswordAsAdminSerializer(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
serializer.update_password()
return Response(status=status.HTTP_200_OK, data={'success': True})
and below is my serializer:
class ResetPasswordAsAdminSerializer(serializers.ModelSerializer):
new_password = serializers.CharField(write_only=True)
password = serializers.CharField(write_only=True)
def validate_password(self, attrs):
self.user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
self.user = request.user
if not self.user or not self.user.check_password(attrs['password']):
raise CustomAPIException(
status_code=status.HTTP_401_UNAUTHORIZED,
message='Invalid password provided',
error_code=None
)
def validate_new_password(self, password):
if not re.match(REGEX['password'], str(password)):
raise CustomAPIException(
status_code=status.HTTP_409_CONFLICT,
message=None,
error_code='password_policy_mismatch'
)
return password
def update_password(self):
self.user.set_password(self.validated_data['new_password'])
self.user.save()
return
class Meta:
model = User
fields = ('password', 'new_password')
But on hitting the api, I am getting the following error:
string indices must be integers
What am I doing wrong?
You don't have to do this in the serializer itself, it can be done in the views pretty easily take a look at this example
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
if not user.check_password(serializer.data.get('old_password')):
return Response({'old_password': ['Wrong password.']},
status=status.HTTP_400_BAD_REQUEST)
# set_password also hashes the password that the user will get
user.set_password(serializer.data.get('new_password'))
user.save()
return Response({'status': 'password set'}, status=status.HTTP_200_OK)
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)

Hook in oauth2 code with DRF

I am trying to build an app which has user login and signup functionality.I can create login and signup both from django, and DRF but could not hook in oAuth2 with DRF to make it functional.I have no idea where should i use it.Should I generate token on signup or login?How can I make it functional?
Here is my code
serializers.py
class UserSerializer(ModelSerializer):
class Meta:
model = User
class UserCreateSerializer(ModelSerializer):
email = EmailField()
username = CharField()
first_name = CharField(required=False)
last_name = CharField(required=False)
password = CharField()
confirm_password = CharField()
class Meta:
model = User
fields = [
'username',
'email',
'first_name',
'last_name',
'password',
'confirm_password'
]
extra_kwargs = {"password": {"write_only": True}}
def create(self, validated_data):
username = validated_data['username']
first_name = validated_data['first_name']
last_name = validated_data['last_name']
email = validated_data['email']
password = validated_data['password']
confirm_password = validated_data['password']
user_obj = User(
username = username,
first_name = first_name,
last_name = last_name,
email = email
)
user_obj.set_password(password)
user_obj.save()
return validated_data
class UserLoginSerializer(ModelSerializer):
username = CharField()
class Meta:
model = User
fields = [
'username',
# 'email',
'password',
# 'token',
]
extra_kwargs = {"password":
{"write_only": True}
}
def validate(self, data):
return data
views.py
class UserCreateAPI(CreateAPIView):
serializer_class = UserCreateSerializer
queryset = User.objects.all()
permission_classes = [AllowAny]
class UserLoginAPI(APIView):
permission_classes = [AllowAny]
serializer_class = UserLoginSerializer
def post(self, request, *args, **kwargs):
data = request.data
print('data',data)
serializer = UserLoginSerializer(data=data)
if serializer.is_valid(raise_exception=True):
new_data = serializer.data
if new_data:
try:
user = User.objects.get(username=data['username'])
print ('user',user)
except ObjectDoesNotExist:
return HttpResponse("Can't find this user")
login(request, user)
return Response(new_data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
tools.py
def get_token_json(access_token):
return JsonResponse({
'access_token':access_token.token,
'expires_in':datetime.now() + timedelta(days=365),
'token_type':'Bearer',
'refresh_token':access_token.refresh_token.token,
'scope':access_token.scope
})
def get_access_token(user):
application = Application.objects.get(name="Foodie")
try:
old_access_token = AccessToken.objects.get(user=user, application=application)
old_refresh_token = RefreshToken.objects.get(user=user, access_token=old_access_token)
except ObjectDoesNotExist:
return HttpResponse('Have not set any token')
else:
old_access_token.delete()
old_refresh_token.delete()
new_token = generate_token()
refresh_token = generate_token()
access_token=AccessToken.objects.create(user=user, application=app, expires=datetime.now() + timedelta(days=365),token=new_token)
RefreshToken.objects.create(user=user, application=app, token=refresh_token, access_token=access_token)
print('aceess',AccessToken)
return get_token_json(access_token)
How can i bridge the gap between DRF and oAuth2 to make login and user registration functional?
Try using python social auth.
Add social.apps.django_app.default to INSTALLED_APPS
Add social.backends.facebook.FacebookOAuth2 to AUTHENTICATION_BACKENDS
Add url(r'^auth/', include('social.apps.django_app.urls', namespace='social')) to your urls.py
However this will work if you have session authentication in your app. If you want to use token based only, then either add a pipe to create the token and send it or take a look at https://github.com/PhilipGarnero/django-rest-framework-social-oauth2

Categories

Resources