Implement Django REST TokenAuthentication for Multiple User Model - django

I need to implement TokenAuthentication using Custom user model Consumer & Merchant (The default User model still exists and is used for admin login).
I've been looking on official DRF documentation and days looking on google, but I can't find any detailed reference about how to achieve this, all of the references I found using default User or extended User models.
class Consumer(models.Model):
consumer_id = models.AutoField(primary_key=True)
token = models.UUIDField(default=uuid.uuid4)
email = models.CharField(max_length=100, unique=True, null=True)
password = models.CharField(max_length=500, default=uuid.uuid4)
class Merchant(models.Model):
merchant_id = models.AutoField(primary_key=True)
token = models.UUIDField(default=uuid.uuid4)
email = models.CharField(max_length=100, unique=True)
name = models.CharField(max_length=100)
password = models.CharField(max_length=500, default=uuid.uuid4)
Settings.py
INSTALLED_APPS = [
...
'rest_framework',
...
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser'
]
}
I'm also using #api_view decorator with function-based views:
#api_view(['POST'])
#renderer_classes([JSONRenderer])
def inbound_product(request, pid):
product = MerchantProduct.objects.get(product_id=pid)

It's recommended to keep authentication data only on one table even if you have multiple user profile tables.So in you case I think you need to have another table for authenticating the users, the table should implement AbstractBaseUser.
And there should be a OneToOne reference between the merchant and customer tables to the new created user model.
In this case your authentication data will only be kept on one place which is the new table .
please check the following docs link for more info regarding custom authentication models and backends

Related

Using Django, SimpleJWT checking User permission to show content belongs to his own company

I am using React(front), Django(server-side), Simple JWT for User Auth Model and Postgres DB.
Let me explain my model: Company owns many properties, and each company has multiple users. As a user logs in, I want them to see a list of properties that belongs to their own company. As the user selects any property from the list, I need to again check if the user has permission and then run the queries. I am currently using Simple JWT for authentication. As the user logs in, the backend generates Access and Refresh tokens. With every request sent from the user side, I am sending JWT access token. On the server side, I want to decode the token, look at what company user belongs to, filter queries accordingly and send the JsonResponse.
Here is my Company Model:
class Company(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='company_user', on_delete=models.CASCADE, null=True)
company_name = models.CharField(max_length=200, null=True)
slug = models.SlugField(max_length=200, unique=True)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
Here is the property model:
class Property(models.Model):
company = models.ForeignKey(Company, related_name='prop_company', null = True, on_delete=models.CASCADE)
property_name = models.CharField(max_length=200, null=True)
property_type = models.CharField(max_length=200, null=True)
def __str__(self):
return self.property_name
Here is the property data
class PropertyData(models.Model):
data = models.ForeignKey(Property, related_name='property_data', null = True, on_delete=models.CASCADE)
lat = model.FloatField(null=True)
long = model.FloatField(null=True)
How should I handle the JWT token in the backend and check which company user belongs to, run queries accordingly and then send a success message along with the filtered data?
In simple-JWT you can customize your token claim. For this add this in your serializer.py file.
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls, user):
token = super().get_token(user)
# Add custom claims
token['company_name'] = user.company_user.company_name # will add requested user company name in token
# ...
return token
# your views.py
class CustomTokenObtainPairView(TokenObtainPairView):
serializer_class = CustomTokenObtainPairSerializer
and also don't forget to update your urls.py file.
from .views import CustomTokenObtainPairView
urlpatterns = [
...
path('api/token/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
...
]
Hope this will solve your problem :)

Django subscriber feature

I am creating a django site/platform where the main concept is users can create shops and other users can subscribe to those who have shops open (think Etsy). Trying to implement the subscriber feature and this is a model I have so far for it:
class Subscriber(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
sub_shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
It works perfect for giving users the ability to subscribe and have the subscribtions listed in their profile and vice versa for shop owners, but for now a user can subscribe to a shop as many times as they want and I would like to prevent this. Idk if there is a constraint to allow multiple subscriber model instances by the same user but not allow for the same exact 'user' and 'sub_shop' instance OR if I am just going on about this in a very bad way!
You can use a UniqueConstraint [Django-doc] to specify that the combination of user and sub_shop should be unique:
class Subscriber(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
sub_shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['user', 'sub_shop'],
name='subscribe_once'
)
]
prior to django-2.2, you can work with unique_together [Django-doc]:
# prior to Django-2.2
class Subscriber(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
sub_shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
class Meta:
unique_together = [['user', 'sub_shop']]
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

How to allow users In Django to see specific data from same model

I am facing a problem while giving permission to admin users in Django that how to restrict users to see only user specific data from same model. I mean that if 2 user can add data to same model, then how to restrict them to access only that data.
in that model you need to specify which user inserted that value. If you have User model, then you can add new field to your model as User which is ForeignKey field.
When you inserted your data with user property, you can easily filter them with user (user.id)
For example if you have Customer model you can filter the value with user's id (in this case it's merchant_id):
customer = Customer.objects.filter(email=email, merchant_id=merchant_id).all()
And our model looks like this:
class Customer(models.Model):
merchant = models.ForeignKey(Merchant, on_delete=models.DO_NOTHING)
name = models.CharField(max_length=128, null=True)
surname = models.CharField(max_length=128, null=True)
email = models.EmailField(max_length=255, null=True)
and you can define a permissions like:
class Task(models.Model):
...
class Meta:
permissions = [
("change_task_status", "Can change the status of tasks"),
("close_task", "Can remove a task by setting its status as closed"),
]
and you can check that via:
user.has_perm('app.close_task')
Hope it helps,

Specify django-rest-framework field for serializer after request?

I've got the following code:
models.py
class Interview(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, help_text="The Primary Key of the Interview")
organization = models.ForeignKey(Organization, on_delete=models.CASCADE, help_text="The Primary Key of the Organization in the interview")
question = name = models.CharField(max_length=200, help_text="The Question")
serializers.py
class InterviewSerializer(serializers.ModelSerializer):
class Meta:
model = Interview
fields = ('organization', 'question')
views.py
class InterviewViewset(viewsets.ModelViewSet):
"""
API endpoint that allows Interviews to be viewed or edited.
"""
serializer_class = InterviewSerializer
Right now, the request body to create an interview requires these fields:
{
"organization": "string",
"question": "string"
}
I'd like the organization for the Interview to automatically be set to the current users organization (request.user.Organization - I've got a custom User model). How can I do this elegantly for my viewset?
One way (probably the best) is overriding save / create / update methods of your ModelSerializer class. DRF docs about it
There you can use self.context.get("request") to get current user. And if you still need organization field to be serialized, just make it read_only=True

Non-user base TokenAuthentication for django-rest-framework

I'm trying to create an api with token authentiaction. My problem is that I don't want tokens to be associated with user accounts but rather an account model.
For example:
class Account(models.Model):
slug = models.SlugField()
name = models.CharField(max_length=255)
website = models.URLField(blank=True, default='')
members = models.ManyToManyField(User, through='AccountMember')
class AccountMember(models.Model):
ADMIN = 1
MANAGER = 2
MEMBER = 3
ROLES = (
(ADMIN, 'administrator'),
(MANAGER, 'manager'),
(MEMBER, 'member'),
)
user = models.ForeignKey(User)
account = models.ForeignKey(Account)
role = models.PositiveSmallIntegerField(choices=ROLES)
date_joined = models.DateField(auto_now_add=True)
class Token(models.Model):
"""
The default authorization token model.
"""
key = models.CharField(max_length=40, primary_key=True)
account = models.ForeignKey(Account, related_name='auth_tokens')
only_allowed_ips = models.BooleanField(default=False)
ip_list = models.TextField(default='', blank=True)
created = models.DateTimeField(auto_now_add=True)
As you can see, there are multiple users associated with an account so assigning the token to them would be useless.
I also want to be able to add multiple tokens to an account.
Does anyone know the best way I could add authentication/permissions to a system like this?
You must replicate the following classes:
django.contrib.auth.backends.ModelBackend: to check for auth and permissions
django.contrib.auth.middleware.AuthenticationMiddleware: to set your Account linked with the request
probably you must create another django.contrib.auth.models.Permission to store your Account related permissions
I just faced the problem myself. What I decided to do is too duplicate django rest framework's authtoken module in my application and to apply my own modifications.
You can copy the folder from DRF's repository in your own application's folder. Be sure to change related fields in your settings.py, application/authtoken/admin.py and of course in your application/authtoken/models.py