User model other than AUTH_USER_MODEL in Django REST Framework - python

I have an architectural problem. I'm using Django (with admin panel) and DRF (api with stateless authentication using JWT).
Django has Admin users represented by model which is more or less the same as default Django User Model. Admins work with Django Admin only and can't use DRF api.
DRF has API users that are allowed to use only api through DRF and can't interact with Django Admin or Django Session etc.
I know that the best approach is to use multi model inheritance, like:
class User(DjangoUserModel):
pass
class Admin(User):
pass
class API(User):
pass
AUTH_USER_MODEL = "User"
but the problem is that, those users are completly different. Eg: API user has complex composite key as an username field which is impossible in combination to simple Admin username field. And many other differences. ..
The question is: may I use a user object that is not an AUTH_USER_MODEL instance in DRF? So self.request.user will store a model instance that isn't connect in any way with an AUTH_USER_MODEL. Has any of you done something similar?

Well, yes sir. You can do that. Look at the following example:
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class AuthenticatedServiceClient:
def is_authenticated(self):
return True
class JwtServiceOnlyAuthentication(JSONWebTokenAuthentication):
def authenticate_credentials(self, payload):
# Assign properties from payload to the AuthenticatedServiceClient object if necessary
return AuthenticatedServiceClient()
And in settings.py:
REST_FRAMEWORK = {
'UNAUTHENTICATED_USER': None,
'DEFAULT_AUTHENTICATION_CLASSES': (
'myapp.authentication.JwtServiceOnlyAuthentication',
),
}
If you want, you can define additional DEFAULT_AUTHENTICATION_CLASSES for your DRF. The authentication classes are just like middleware, just a queue that is populating request.user.
Adding your own authentication classes that are using diffrent user model from AUTH_USER_MODEL will work exactly as you would except.

Since you are using Django and DRF, perhaps you can write an APIUser model which extends from AbstractBaseUser with your customizations, write a custom authentication class and plug that into the REST_FRAMEWORK.DEFAULT_AUTHENTICATION_CLASSES setting. Leave AUTH_USER_MODEL alone for the django admin.
Your custom authentication may just need to override authenticate_credentials (i've referenced the TokenAuthentication class in DRF github) and return an APIUser rather than the default defined in settings.AUTH_USER_MODEL. This will be a bit different because you're decoding a JWT, so you'll likely be extract some info from your JWT and looking up your APIUser by whatever means you need, such as your composite field. This should result in self.request.user being an APIUser for your DRF API views.
You're API views should be using the rest framework's settings, and your Django admin should be using the regular django settings. There may be some other caveats, but generally you'll be ok with just this I think.

One thing that I immediately recall is how Mongoengine used to hack the whole django authentication system. Mongoengine < 0.10 has a django compatibility app that implements a crutch to store users in MongoDB and make them accessible via self.request.user.
It has to use a crutch, because Django Session API is opinionated and assumes that you're using AUTH_USER_MODEL, backed by SQL database for storing your users.
So, I think you should disable SessionMiddleware and CSRF token handling and use 2 distinct custom authentication systems for Admin and API purposes. With TokenAuthentication or JWTAuthentication this should be doable.
Here's another example of a project with DRF and Mongoengine, with a custom implementation of TokenAuthentication, backed by MongoDB.

Related

Django form validation vs DRF serializer validation

I'm new to APIs creation in django.
I checked DRF serializers and I found them useful when manipulating models in DB. My APIs don't use models they are based on forms.
So my question is : what can I get more from using DRF validators then using django forms validators in my views ? maybe the content type is more convenient (application/x-www-form-urlencoded vs application/json) ?
DRF's serializers are nicely integrated within the framework. Whether they are models based or not, you will have a nice browsable UI to play with for free and you can get OpenAPI schema generation. There are probably other things I did not think of.
What serializers can do that forms can't:
Nested data - although you can get some results with formsets.
Hyperlinked relational fields
Difference between an empty field and a blank field - HTML forms can't do that
out of the box integration with generic class based views or viewsets
Please note that your can still benefit from DRF even if you are not using serializers. DRF parts are loosely coupled.

Django Multiple Auth Models

I am working on a project where I need to have 3 types of Users.
Admin
Vendor
Customer
I want to be having seperate Models for all three of them Vendor & Customers instead of having a type field in a common User Model.
My first approach to this problem was to define all models by sub-classing the AbstractUser model
# models.py
from django.contrib.auth.models import AbstractUser
class Customer(AbstractUser):
pass
class Vendor(AbstractUser):
pass
And add a custom Authentication backend to handle the authentication of users based on the request object.
# settings.py
AUTHENTICATION_BACKENDS = ['backends.CustomAuthBackend']
And my backends.py file will contain the logic to authenticate users and use different models for each one based on the request object.
# backends.py
from __future__ import print_function
class CustomAuthBackend(object):
def authenticate(self, request, username=None, password=None):
# authenticate user based on request object
def get_user(self, user_id):
# logic to get user
However this does not work and looks like i also need to specifiy the AUTH_USER_MODEL in settings.py which is used for authentication.
Is it possible at all.Does Django allow to authenticate from 3 different tables. How can I go ahead with this ? Is there any other approach to this and what should I change ?
I have done similar staff a couple days ago, you are in the right approach. But there are a few other things needed to change to make it work. I'll explain what I did to make it success.
First, you have to custom your own user model, and you have to do it in the first place before you make the migrations. And also in the model file define different userManagers to manager different type of users. Then in your settings file you have to set AUTH_USER_MODEL and AUTHENTICATION_BACKENDS, AUTH_USER_MODEL is the default user model django will use for authentication and you can only set to one user model, but for AUTHENTICATION_BACKENDS you can have multiple backends,it's a list and django will loop every option inside to authenticate. by default it use django.contrib.auth.backends.ModelBackend, you can add your own auth backends. Check this on how to make your own authentication backend:https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#writing-an-authentication-backend.
And depends on your application, you may also need to custom the serializer function and override some classes to make it work. In my own application, I was using DRF and JWT token to authenticate, so I also override some of the function which by default use the AUTH_USER_MODEL variable. In the end, I'm able to use admin model to login the admin page and use another custom user model to authenticate the application and get the JWT token. Anyway, always reference this page: https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#customizing-authentication-in-django. Hope this can help you in your application.
django.contrib.auth is designed to work with one UserModel. Depending on what you want to achieve there are different apporaches.
If you just want to store different kind of profile/meta data for the different types of user you might use multi table inheritance - in this case you might stick with the default user model.
When it comes to different permissions based on the user type you should not solve this using separate classes. Instead use groups. This approach is much more flexible. It can nearly always happen that one person should belong to more than one group of users. When you model this based on the user classes you are in trouble.

Multi-tenant issue using django-rest-framework

We have an issue to make a multi-tenancy SaaS application using Django and django-rest-framework.
We would like to route to the correct database according to the url, and using django-rest-framework serializers.
But with django-rest-framework, we cannot specify the database we want to save to with serializers. We are able to request data by overriding the get_queryset method, and with the using(<database>) function.
class SomeSerializer(serializers.ModelSerializer):
def get_queryset(self):
return SomeModel.objects.using(self.database)
If we want to save an instance to a custom database, we cannot use ModelSerializer, we have to write each serializer using the Serializer class.
We can make a pull request to add the option to django-rest-framework Multiple databases support #65 - django-rest-framework
Or, we can do something similar to this snippet : Database Routing by URL, it's quite old (2010) but still working. But we are not very comfortable with the idea of using the local thread to transfer variables.
How do we do this properly in 2017 ? Are there other options ?

Check if user is authenticated in urls.py

Is it possible to do something like this in urls.py?
urls.py: http://pastebin.com/raw.php?i=QVQ8BEvJ
This is not right way to implement it.
The best solution is to wrap your views with #login_requered decorator.
Example:
#login_required
def home_page(request):
...
You can specify in your settings.py LOGIN_URL where user will be redirected if not authorized.
Please read more at django documentation
Authentication checks should be performed in the view, that's where checks and security are ment to exist.
Apart from that you can decorate Class Based Views in urls to check for login, but the most common / sensible / usual approach is to make that check in the view.
If you are using Class Based Views then you could create a Mixin which checks if the user is authenticated, this combined with the tools Django Offers, allow you to redirect the user to the appropriate view (login, or if check is passed allow him to view the requested url).
If you are using function based views, then Django offers decorators for those functions to provide common "needs" of a web framework.
PS If you are using CBV's then django-braces has already a few wonderful mixins available for common tasks (Ajax, Security etc).

Django Project structure, recommended structure to share an extended auth "User" model across apps?

I'm wondering what the common project/application structure is when the user model extended/sub-classed and this Resulting User model is shared and used across multiple apps.
I'd like to reference the same user model in multiple apps.
I haven't built the login interface yet, so I'm not sure how it should fit together.
The following comes to mind:
project.loginapp.app1
project.loginapp.app2
Is there a common pattern for this situation?
Would login best be handled by a 'login app'?
Similar to this question but more specific.
django application configuration
UPDATE
Clarified my use-case above.
I'd like to add fields (extend or subclass?) to the existing auth user model. And then reference that model in multiple apps.
Why are you extending User? Please clarify.
If you're adding more information about the users, you don't need to roll your own user and auth system. Django's version of that is quite solid. The user management is located in django.contrib.auth.
If you need to customize the information stored with users, first define a model such as
class Profile(models.Model):
...
user = models.ForeignKey("django.contrib.auth.models.User", unique=True)
and then set
AUTH_PROFILE_MODULE = "appname.profile"
in your settings.py
The advantage of setting this allows you to use code like this in your views:
def my_view(request):
profile = request.user.get_profile()
etc...
If you're trying to provide more ways for users to authenticate, you can add an auth backend. Extend or re-implement django.contrib.auth.backends.ModelBackend and set it as
your AUTHENTICATION_BACKENDS in settings.py.
If you want to make use of a different permissions or groups concept than is provided by django, there's nothing that will stop you. Django makes use of those two concepts only in django.contrib.admin (That I know of), and you are free to use some other concept for those topics as you see fit.
You should check first if the contrib.auth module satisfies your needs, so you don't have to reinvent the wheel:
http://docs.djangoproject.com/en/dev/topics/auth/#topics-auth
edit:
Check this snippet that creates a UserProfile after the creation of a new User.
def create_user_profile_handler(sender, instance, created, **kwargs):
if not created: return
user_profile = UserProfile.objects.create(user=instance)
user_profile.save()
post_save.connect(create_user_profile_handler, sender=User)
i think the 'project/app' names are badly chosen. it's more like 'site/module'. an app can be very useful without having views, for example.
check the 2008 DjangoCon talks on YouTube, especially the one about reusable apps, it will make you think totally different about how to structure your project.

Categories