Django custom authentication - python

I am using Django REST Framework in my app. I need authentication but not the default one. I have a model:
class Worker(models.Model):
token = models.CharField(...)
ip = models.GenericIPAddressField(...)
created_date = models.DateTimeField(...)
last_update = models.DateTimeField(...)
Worker sends messages through my API to view WorkerView which inherits from Django REST Framework's APIView. Token is send in request's header:
class WorkerView(APIView):
def post(self, request):
# some not important code
I have an authenticating method:
def authenticate(request):
try:
ip = request.META.get("REMOTE_ADDR", None)
token = request.META.get("HTTP_AUTHORIZATION", None)
...
I thought about two solutions:
Make a mixin class and inherit it in my WorkerView:
class WorkerView(AuthenticationMixin, APIView)
...
Make a class decorator from my authenticate method and use it like this:
#authenticate
class WorkerView(APIView)
But in both cases I need to pass request argument to authenticate method.
How to do that? Or maybe there's a better solution to my problem?

Why don't You create a custom authentication class as defined here?
http://www.django-rest-framework.org/api-guide/authentication/#custom-authentication

Related

Changing the default user while using REST framework JWT

I have a proxied user model:
class TheDude(User):
class Meta:
proxy = True
And I'm using the Django REST framework JWT to do JWT auth in the REST API.
I'd like to get the user object from the request but currently it's a User object. Because it's proxied I can't use AUTH_USER_MODEL. I've tried doing a middleware component to override the user in the request but it's not set at that stage. I've also tried using JWT_RESPONSE_PAYLOAD_HANDLER however my function isn't called so I can't set it there either.
If I want to be able to get TheDude object when I call request.user instead of User in my views, how would I do this while authing using the REST framework JWT Auth library?
EDIT
I've added
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'myapp.authentication.MyCustomJWTAuthentication',
)
...
}
to my settings.py and my
class MyCustomJWTAuthentication(JWTAuthentication):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.user_model = TheDude
Which is called correctly however when I get the user from request in my serialiser, it's still of type User and not TheDude
class TestSerializer(serializers.ModelSerializer):
user_test = serializers.SerializerMethodField('get_user_test')
def get_user_test(self, obj):
print(type(self.context['request'].user))
It should be possible to use the proxy model by overriding the JWTAuthentication authentication class and setting your proxy user model as the user_model like so:
from rest_framework_simplejwt.authentication import JWTAuthentication
class MyCustomJWTAuthentication(JWTAuthentication):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.user_model = TheDude
Say you add this class on myapp/authentication.py, you can then apply this custom authentication class as one of the default authentication classes in your REST_FRAMEWORK settings:
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': (
'myapp.authentication.MyCustomJWTAuthentication',
...
)
...
}
Or just apply it to certain views you want:
from myapp.authentication import MyCustomJWTAuthentication
class CertainAPIViewThatNeedsTheDude(APIView):
authentication_classes = (MyCustomJWTAuthentication, )
This should in turn give you a request.user that is a TheDude instance.

Using a controller to return different views in a django app

I want to return multiple views in my app according to the logic in a controller which is connected to the url resolver.
Here is some example code of the idea that I have .
class Project(Singleton):
TYPE1, TYPE2 , TYPE3 = (0,1,2)
def init(self,request, slug):
self.pengine = ProjectEngine()
self.pengine.init()
self.request = request
self.slug = slug
ptype = pengine.getProjectType()
return self.showProjectView(ptype)
def showProjectView(self, projectType):
if(projectType == TYPE1):
return Type1View.as_view(self.request, self.slug)
elif(projectType == TYPE2):
return Type2View.as_view(self.request, self.slug)
else:
return Type3.as_view(self.request, self.slug)
Type1View for example extends from Djangos default TemplateView class. ProjectEngine is supposed to be the model class to get the data from.
The url resolver would then get the callable view from the init method, either using the Singleton idea that I have here, or using a class only method the same way as how a base View class is implemented.
I'm just not sure if I should be using the MVC pattern this way. The url resolver is supposed to resolve directly to the view class methods, and I want to send that data from the controller , using the model class to the view. How do I achieve this?
Does the as_view method have a way to send data through to it? Or should I let go of Django's View class and make another View here that corresponds to the design.

Django Test mock instance of module variable

I'm trying to test my Django app which has a proxy API which is instantiated in its own module.
api.py
class ProxyApi(object):
def __init__(self, server_info):
pass
def validate_login(self, credentials):
# call to real api here
api = ProxyAPi()
middlewares.py
from mymodule.api import api
class MyMiddleware(MiddlewareMixin):
def process_request(self, request):
if api.validate_login():
# do something with proxy api
views.py
from mymodule.api import api
class TaskView(LoginRequiredMixin, FormView):
def get(self, request):
if api.validate_login():
# do something with proxy api
tests.py
class InputTasksViewTest(TestCase):
#mock.patch('mymodule.api.ProxyAPi')
def test_add(self, mock_api):
mock_api.validate_login.return_value = True
response = self.client.get(reverse('task'))
The original validate_loginis still called.
I would like to know how to handle the instantiation of ProxyApi while still retaining mocking capacity.
Ok I found my own solution, the problem was that once Django started, it read some files (like models or views or middlewares) that automatically instantiated api variable from import.
I just needed to defer this instantiation so I can mock the ProxyApi object, here's what I did:
api = SimpleLazyObject(lambda: ProxApi())
You have def validate_login(self, credentials): in api
and in middleware you define below code. So How you will send creadentials to API from middleware api.validate_login(<You should send credentials to api as parameter>):
from mymodule.api import api
class MyMiddleware(MiddlewareMixin):
def process_request(self, request):
if api.validate_login():
pass

Django-tastypie pass request.user to custom save method

Since my model's custom save method takes request.user as an argument I'm unable to do POST/PUT requests.
TypeError at /api/obsadmin/observation/23
save() takes at least 2 arguments (1 given)
I'm using SessionAuthentication() and have included the CSRF token.
Here's the relevant model part:
def save(self, user, owner=None, *args, **kwargs):
self.updated_by = user.id
super(ObsModel, self).save(*args, **kwargs)
And the resource:
class ObservationResource2(ModelResource):
comments = fields.ToManyField(CommentResource2, 'comments', full=True, null=True)
class Meta:
queryset = Observation.objects.filter(is_verified=True)
authentication = SessionAuthentication()
authorization = DjangoAuthorization()
resource_name = 'observation'
always_return_data = True
I've just achieved this same end goal by using the built-in hydrate methods to modify the data prior to saving. The current request is available in bundle.request inside the hydrate methods. See the docs here.
I have a Friend model exposed via FriendResource that I want to link to the creating Django user via a user ForeignKey field.
My example Resource code:
class FriendResource(ModelResource):
class Meta:
queryset = Friend.objects.all()
resource_name = 'friend'
excludes = ['slug',]
authentication = SessionAuthentication()
authorization = DjangoAuthorization()
always_return_data = True
def get_object_list(self, request):
return super(FriendResource, self).get_object_list(request).filter(user=request.user)
def hydrate(self, bundle):
bundle.obj.user = bundle.request.user
return bundle
Hope that helps!
You could override the default save() method on your ModelResource subclass. Looking at the default implementation shows that save() is called with a bundle object which has both the request and the object to be saved.
Unfortunately, there's no easy way to change this without copying most of that code because changing a Django model's save() signature is fairly uncommon. You might be able to do something like this, although I'd recommend testing it carefully:
from functools import partial
try:
old_save = bundle.obj.save
bundle.obj.save = partial(old_save, user=bundle.request.user)
return super(FooResource, self).save(bundle)
finally:
bundle.obj.save = old_save
References:
obj_create: docs source
obj_update: docs source
save: source

Store objects at "request scope" in google app engine

I would like to store some information at the "request scope" when using google app engine (python). What I mean by this is that I would like to initialize some information when a request is first received, and then be able to access it from anywhere for the life of the request (and only from that request).
An example of this would be if I saved the current user's name at request scope after they were authenticated.
How would I go about doing this sort of thing?
Thanks!
A pattern used in app engine itself seems to be threading.local which you can grep for in the SDK code. Making os.environ request local is done like that in runtime/request_environment.py for example.
A rough example:
import threading
class _State(threading.local):
"""State keeps track of request info"""
user = None
_state = _State()
From elsewhere you could authenticate early on in handler code.
from state import _state
if authentication_passed:
_state.user = user
and provide convenience that can be used in other parts of your code
from state import _state
def get_authenticated_user():
user = _state.user
if not user:
raise AuthenticationError()
return user
You need something like this:-
class BaseHandler(webapp2.RequestHandler):
#A function which is useful in order to determine whether user is logged in
def initialize(self, *a, **kw):
#Do the authentication
self.username = username
class MainHandler(BaseHandler):
def get(self):
print self.username
Now if you inherit BaseHandler class all the request will first go through the initialize method of BaseHandler class and since in the BaseHandler class you are setting the username
and MainHandler inherits form BaseHandler you will have the self.username defined and all the request wil go through initialize method.

Categories