Django Rest Framework custom token authentication - python

I want a custom authentication which will authenticate the token in my model.I have a model called User_Auth where there is a field called 'secret_token'. I want the Django Rest Framework to Authenticate token through that model.
I had followed the docs on django rest framework website but didnt work for me.
Please help me to solve this issue.
models.py
class User_Auth(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User_Master, on_delete=models.CASCADE)
secret_token = models.CharField(max_length=2000, null=True, blank=True)
authentication.py
class UserAuthentication(authentication.TokenAuthentication):
def authenticate(self, request):
secret_token = request.META.get('Authorization')
if not secret_token:
return None
try:
ua = User_Auth.objects.get(secret_token=secret_token)
except User_Auth.DoesNotExist:
raise exceptions.AuthenticationFailed('Unauthorized')
return (ua, None)
views.py
class User(viewsets.ModelViewSet):
authentication_classes = (UserAuthentication,)
permission_classes = (IsAuthenticated,)
I am passing the token through the Header.
Authorization: 'a7c14fc808b58533e4d1651cd2375c3b41a38ef5d120254a1cb4bbd90b3be1534215516b023818e4'
Its Returning this error
'User_Auth' object has no attribute 'is_authenticated'
Please help me.

You are missing is_authenticated property in User_Auth model. This is a read only attribute and always return True for a valid user. Django's User class has this attribute and internally it has been used for authentication.
So to resolve the error add this attribute in User_Auth class like following.
class User_Auth(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User_Master, on_delete=models.CASCADE)
secret_token = models.CharField(max_length=2000, null=True, blank=True)
#property
def is_authenticated(self):
"""
Always return True. This is a way to tell if the user has been
authenticated in templates.
"""
return True

Related

Django: Block access to not authorized users in CreateView

I have a problem for block access to not authorized user in pages dedicated to add new objects. List of that users is stored in many-to-many field in project object and in foreign key field.
Below is models.py
class Project(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="projects_as_owner", null=True)
project_managers = models.ManyToManyField(User, related_name="projects_as_pm", blank=True)
name = models.CharField(max_length=200)
description = models.TextField(blank=True)
date_of_insert = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Milestone(models.Model):
project_fk = models.ForeignKey(Project, related_name="milestones", on_delete=models.CASCADE)
name = models.CharField(max_length=200)
description = models.TextField(blank=True)
date_of_insert = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
And views.py with class I have problem
class NewMilestone(LoginRequiredMixin, generic.CreateView):
model = Milestone
fields = ['name', 'description']
lookup_url_kwarg = 'p_id'
template_name = 'main/new_milestone.html'
# ... two functions, that work good, not important here ...
def get_queryset(self):
qs = super(NewMilestone, self).get_queryset()
project = Project.objects.get(id=self.kwargs['p_id'])
if(qs.filter(project_fk__owner=self.request.user).exists() or User.objects.filter(id=self.request.user.id).filter(projects_as_pm__id=project.id).exists()):
return qs
else:
return Http404("You are not authenticated to this action")
Objective here is here to allow authenticated users (owner and project manager/s) to enter this view and for anybody else show info about declined access.
Problem is that, that method, get_queryset, doesn't block unauthorised users in CreateViev class.
I tried some configurations for that issue, every single one I used had this flaw.
My question here is how to make it work the way I expect from it?
PS. English is not my native language and it was a while since I wrote something, so please be understanding.
You are using the LoginRequiredMixin which is a good thing. But then you didn't set any of the parameters available.
LoginRequiredMixin inherits from AccessMixin and you can use all it's parameters with which it shouldn't be too complicated to cover your case.
Here's a possible implementation:
class NewMilestone(LoginRequiredMixin, generic.CreateView):
...
# your class attributes
...
raise_exception = True
# Returns a permission denied message. Default: empty string
def get_permission_denied_message(self):
return "Access is restricted to authenticated users"
If you have raise_exception set to True then the get_permission_denied_message method will be called. Otherwise the user will be redirected to the login_url which you also would have to declare as a class attribute.

How to obtain a user instance in django application

I'm trying to obtain a user instance for the profile page in django app but I'm finding some difficulties implementing that functionality. I have the following blocks of code:
models.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, primary_key=True, on_delete=models.CASCADE, default="")
image = models.ImageField(upload_to="images/user_profile_pics/", default="images/default_profile_pics/default.jpg")
firstname = models.CharField(max_length=50)
lastname = models.CharField(max_length=50)
def __str__(self):
return f'{self.lastname} profile'
serializers.py
class user_profile_serializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
views.py
class user_profile(generics.GenericAPIView):
serializer_class = user_profile_serializer
def get(self, request, *args, **kwargs):
user = self.get_serializer(request.user).data
if request.user.is_authenticated:
return Response(user, status=status.HTTP_200_OK)
else:
pass
urls.py
urlpatterns = [
path('profile/', user_profile.as_view(), name="user-profile"),
]
When ever I assess the profile url, I get an error message 'AnonymousUser' object has no attribute 'data' I have tried a couple of approaches but none worked. Please, how do I obtain a specific user from the database?
request.user is AnonymousUser when the user is not logged in. In that case that object does not have data attribute. Hence the error you get. One thing you can do is check request.user.is_authenticated and if the user is not authenticated, return some other value / or None. And try logging in before trying to access the user.data value.

Bookmarking function django/python

I'm looking to create a model for users to bookmark a recipe. I have the below:
models.py
class RecipeBookmark(models.Model):
recipe = models.ForeignKey(
Recipe, on_delete=models.PROTECT, related_name="bookmarks"
)
bookmarked_by = models.ForeignKey(User, on_delete=models.PROTECT)
bookmarked_at = models.DateTimeField(auto_now_add=True)
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = ["username", "email", "date_joined"]
class RecipeBookmarkSerializer(serializers.ModelSerializer):
bookmarked_by = UserSerializer(read_only=True)
class Meta:
model = models.RecipeBookmark
fields = ["recipe", "bookmarked_by", "bookmarked_at"]
def create(self, validated_data):
request = self.context["request"]
ModelClass = self.Meta.model
instance = ModelClass.objects.create(
**validated_data, **{"bookmarked_by": request.user}
)
return instance
views.py
#permission_classes([IsAuthenticated])
class RecipeBookmarkView(generics.CreateAPIView):
queryset = models.RecipeBookmark.objects.all()
serializer_class = RecipeBookmarkSerializer
urls.py
path("recipes/bookmarks/", PublishedRecipeBookmarkView.as_view()),
I want to perform a lookup, given the recipe id through a POST request, to add the user to the bookmarks field, if the user already exists in the bookmarks field, to remove that user form the field (remove the bookmark). Many users can bookmark a given recipe.
Also, How can a lookup be performed to return recipes that a logged in user has bookmarked via an api endpoint?
Current error with get_or_create():
Error: Internal Server Error
Response body
Download
AttributeError at /api/recipes/bookmarks/
Got AttributeError when attempting to get a value for field recipe on serializer RecipeBookmarkSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the tuple instance.
Original exception text was: 'tuple' object has no attribute 'recipe'.
If you want your serializer to ensure that only one bookmark is created per user per recipe, you can use get_or_create:
def create(self, validated_data):
request = self.context["request"]
ModelClass = self.Meta.model
instance = ModelClass.objects.get_or_create(
**validated_data, **{"bookmarked_by": request.user}
)
return instance
If the bookmark is already present, it will just grab it and return.
Also, How can a lookup be performed to return recipes that a logged in user has bookmarked via an api endpoint?
To support this, you can define ListCreateAPIView to your view and override the queryset like so:
class RecipeBookmarkView(generics.ListCreateAPIView):
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(bookmarked_by=self.request.user)
This will then support getting all the RecipeBookmark that is owned by the current user via GET requests on recipes/bookmarks/

Django - QuerySet is not iterable in permissions.py

New to Django and Django Rest here.
I have the following structure :
class MyUsers(AbstractUser):
email = models.EmailField(unique=True)
USERNAME_FIELD = 'email'
class roles(models.Model):
id = models.AutoField(primary_key=True)
label = models.CharField(max_length=80)
class user_roles(models.Model):
id_user = models.ForeignKey(MyUsers,on_delete=models.CASCADE)
id_role = models.ForeignKey(roles,on_delete=models.CASCADE)
I'm trying to create a custom permission, to allow users with a specific role to access some endpoints.
class IsAuthenticatedAndLeader(BasePermission):
def has_permission(self, request, view):
id_role=models.roles_users.objects.filter(id_user=request.user.id).values_list('id_role',flat=True)
if "Leader" in models.roles.objects.filter(id=id_role).values_list('label',flat=True):
return request.user and request.user.is_authenticated
def has_object_permission(self, request, view, obj):
return True
When I try to access to the endpoint, I have the following error:
TypeError: argument of type 'QuerySet' is not iterable
However if I try in views.py something simple like the following it works :
if "Leader" in models.roles.objects.filter(id=3).values_list('label',flat=True):
print("Yes")
So I'm not sure why I'm getting this error when trying to apply it to permissions.py
I think your problem its
id_role=models.roles_users.objects.filter(id_user=request.user.id).values_list('id_role',flat=True)
if "Leader" in models.roles.objects.filter(id=id_role).values_list('label',flat=True):
id_role is a list and you can get roles where id=(ids....)
then it not works, try with:
if "Leader" in models.roles.objects.filter(id__in=id_role).values_list('label',flat=True):

Prevent create/update foreign key that refers to forbidden objects in Django rest framework

Let us assume I have the following models
class Blog(models.Model):
user = models.ForeignKey(User, null=False, on_delete=models.CASCADE)
class Post(models.Model):
blog = models.ForeignKey(Blog, null=False, on_delete=models.CASCADE)
post = models.TextField()
The problem is that creating a Post will let me set the blog id to anything (that exists). That means that I am able to create a Post object that has a relation to a Blog object that the user does not "own".
However, there are easy and documented ways to prevent the user from accessing objects via the GET method, that are forbidden to them, by filtering the queryset and using check_object_permissions. Example:
class PostViewSet(viewsets.ModelViewSet):
serializer = PostSerializer
def get_queryset(self):
return Post.objects.filter(blog__user=self.request.user)
def check_object_permissions(self, request, obj):
if obj.user != request.user:
raise exceptions.PermissionDenied()
super().check_object_permissions(request, obj)
How do I solve my above issue and prevent creating relations to forbidden objects the smartest/correct way in Django REST framework?
You can add blog validation to the serializer (check Field-level validation doc) and raise error if user dnt have permission to the selected blog:
class PostSerializer(serializers.ModelSerializer):
...
def validate_blog(self, value):
request = self.context['request']
if value.user != request.user:
raise serializers.ValidationError('Blog id doesn't exist')
return value

Categories