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
Related
I am using djangorestframework-simplejwt==4.4.0 in my application for User Authentication. But by default it provides multiple logins for a single user i.e. I can generate n number of tokens.
What I want is to prevent multiple logins from the same account. How do I do that?
Models.py
class Company(models.Model):
region_coices = (('East', 'East'), ('West', 'West'), ('North', 'North'), ('South', 'South'))
category = (('Automotive', 'Automotive'), ('F.M.C.G.', 'F.M.C.G.'), ('Pharmaceuticals', 'Pharmaceuticals'),
('Ecommerce', 'Ecommerce'), ('Others', 'Others'))
type = models.ManyToManyField(CompanyTypes)
name = models.CharField(max_length=500, default=0)
email = models.EmailField(max_length=50, default=0)
class User(AbstractUser):
is_admin = models.BooleanField(default=False)
company = models.ForeignKey(Company, on_delete=models.CASCADE, blank=True, null=True)
#property
def full_name(self):
return self.first_name + " " + self.last_name
class EmployeeTypes(models.Model):
emp_choices = (('Pool Operator', 'Pool Operator'), ('Consignor', 'Consignor'), ('Consignee', 'Consignee'))
emp_type = models.CharField(max_length=500, default='Pool Operator', choices=emp_choices)
class Employee(models.Model):
role_choices = (('CRUD', 'CRUD'), ('View', 'View'))
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="company")
name = models.CharField(max_length=500, default=0)
Urls.py
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
You need to implement a login mechanism yourself.
You can store user refresh token with his user agent and IP address in the database or
some cache systems like Redis, when the user wants to refresh the access token, check provided refresh token alongside his IP address and user agent against the database and if any token exists, give him a new access token.
On login, drop unexpired refresh tokens if exist in the database for the user that has a successful login, and then insert the new refresh token you just generated, in the database.
By implementing this solution, if the user tries to log in again, he will be automatically logged out from previous logins.
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
I am building an API with Django rest, and I am dealing with a situation where I have to authenticate users with the same username but in different organizations and I have no idea how to do it, any suggestions ?? I would be grateful.
User model :
class User(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
email = models.EmailField(_('email address'), unique=True)
phone_number = models.CharField(max_length=191, blank=True, null=True)
roles = models.ManyToManyField(to=Role,db_column="nom",blank=True)
objects = CustomUserManager()
multiple_connections = models.BooleanField(default=True)
organization = models.ForeignKey(
to=Organizations,on_delete=models.CASCADE,to_field="organization_key", blank=True, null=True)
def __str__(self):
return self.username
class Meta:
db_table = 'users'
verbose_name_plural = "Users"
unique_together = ('useranme', 'organization',)
Two ways I can think of: you'll need to have users specify their organization as one of the parameters for your authentication API endpoint(s), or provide different authentication API endpoints to users from different organizations.
If you go the first route, you might have everyone POST to /my_api/auth/, and send a payload like:
{
'username': 'Jim123',
'password': 'password123456!',
'organization_id': 7
}
If you go the second route, you would have multiple endpoints like /my_api/organization_a/auth/, my_api/organization_b/auth/, etc.
In both cases, you'll have to have your server-side authentication code make sure it's comparing username/password against users in the correct organization (presumably the organization is stored in your database somewhere).
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,
I have a scenario where in my users profile they have an associated organisation. I need to be able to allow the users to select and set this organisation (user_organization), however I would like to do it without allowing them to just see a list (drop down menu) of all the organisations within the application. My work around for this was to issue each organisation with a unique code (org_code) and allow users to enter that code into a form and have the related organisation applied to their profile. I can easily understand the suedocode logic behind this, however I am unsure how to implement it within my views and forms. If anyone can advise me the best way to do this or point me in the correct direction to learn how? It would be appreciated. See my models below for clarification on how things fit together.
Profile:
class Profile(models.Model):
Super_Admin = "Super_Admin"
Admin = "Admin"
Manager = "Manager"
Developer = "Developer"
ROLE_CHOICES = (
(Super_Admin, 'Super_Admin'),
(Admin, 'Admin'),
(Manager, 'Manager'),
(Developer, 'Developer'),
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
user_group = models.OneToOneField(Group, on_delete=models.CASCADE, blank=True, null=True)
user_organization = models.OneToOneField(Organization, on_delete=models.CASCADE, blank=True, null=True)
role = models.CharField(choices=ROLE_CHOICES, default="Developer", max_length=12)
activation_key = models.CharField(max_length=120, blank=True, null=True)
activated = models.BooleanField(default=False)
def __str__(self):
return self.user.username
Organizations:
class Organization(models.Model):
org_name = models.CharField(max_length=120, blank=False, unique=True)
org_code = models.CharField(max_length=120, blank=False, unique=True, default=GenerateOrganozationCode)
Answering using the extra info from the comments you've made above:
"I want them to be able to input a code into a text field which when submitted, if it matches the code (org_code) in the organization model it will then populate the (user_organization) in their Profile with the correct (org_name)"
Within the logic for the view, you need to extract the org_code. You should also make org_code of Organization the primary key of the object (you dont have to, but it would be easier if the pk was the org code). From here, you can map the org_code with the primary key value of Organization.
Organization.objects.get(pk=the_entered_org_code)
If you'd rather not assign org code as the primary key of the object, you can just filter for the org code.
Organization.objects.filter(org_code=the_entered_org_code)
This should get you started.