I'm trying to make throttling on OTP authentication so user can only send one message every minute
class BurstRateThrottle(AnonRateThrottle, UserRateThrottle):
scope = 'burst'
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES':
('rest_framework_simplejwt.authentication.JWTAuthentication',),
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'burst': '1/min',
'anon': '200/min',
'user': '250/min',
},
#api_view(['POST'])
#permission_classes([AllowAny])
#throttle_classes([BurstRateThrottle])
def login_send_token(request):
...
The problem with this is that the api gets throttled even when the phone number is wrong so I'm trying to only throttle when the OTP message is send or when the response is 200 or 201
Is there any way to access the response status code in allow_request method?
or to manually execute the throttle from the function that call twilio api?
The allow_request method is run before the method, so you only have the result of the API request after the framework decides whether the request can be run or not.
One way to do this might be to make the request to the API within the allow_request method and only add the request to the history of requests if the API request succeeded.
That does mix up where you would expect to find that code though, so an alternative would be to build the custom throttling directly into your login_send_token method.
I solved this by converting login_send_token to a class based view
class LoginSendToken(APIView):
permission_classes = [AllowAny]
throttle_classes = [BurstRateThrottle]
after that I overrode initial method and commented out the self.check_throttles(request) calling
def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
self.perform_authentication(request)
self.check_permissions(request)
# self.check_throttles(request)
and called check_throttles from my post method after serializer validation
now it only checks throttling if the serializer was valid
Related
I have a viewset for services and an action method defined as:
#action(
methods=['patch'], detail=True, url_name='deploy', url_path='deploy')
def deploy(self, request, pk=None):
"""
An extra action for processing requests
"""
# Gets current instance
instance = self.get_object()
[...]
# Sends GET request
get = APIServiceRequestRouter()
item_list = get.get_data_request()
My get_data_request() method will send a get request to a url, which will return a json response. But of course, this goes out to a third-party api, which I want to mock in my unit test.
I have this unit test for my deploy action endpoint on my services viewset, which currently only test the send of a patch payload to the endpoint. So I need to add a mocked get response as well:
def test_valid_deploy(self) -> None:
"""Test an valid deploy"""
mock_request
response = self.client.patch(
reverse('services-deploy', kwargs={'pk': self.service2.pk}),
data=json.dumps(self.deploy_payload),
content_type='application/json'
)
self.assertEqual(response.status_code, status.HTTP_206_PARTIAL_CONTENT)
What I'm not sure is how to add a mocked get response into this unit test. The error I'm getting when I run my unit tests is that in my test_valid_deploy is that the get_data_request() method I have this:
response = http_get_request.delay(api_url)
response_obj = response.get()
item_ids = response_obj['item_id']
But of course response_obj is empty or of NoneType and therefore the ['item_id'] key does not exist, I get a TypeError: 'NoneType' object is not subscriptable error.
My thoughts then turned to mocking the get to populate response_obj with relevant data so that my test passes.
Any thoughts or help would be very much appreciated.
Try mocking the return value of get_data_request instead?
#mock.patch('path.to.APIServiceRequestRouter.get_data_request')
def test_valid_deploy(self, mocked_get_data_request):
mocked_get_data_request.return_value = {"key": "value"} # your mocked object here
# rest of the test...
I'm really stuck at this problem for a couple of days now.
While I understand, what's happening here, I don't really know the best workaround/solution for this.
Problem:
I'm trying to create a user login endpoint using Django and DRF in general.
My login API needs to support a login via password as well as login via OTP.
My LoginView looks like:
def post(self, request, **kwargs):
"""
post
Method to handle user login
:param request:
:param args:
:param kwargs:
:return:
"""
request_data = request.data
login_using_password = request_data.get('login-with-password') is True
login_using_otp = request_data.get('login-with-otp') is True
if request_data is not None:
if all((login_using_password, login_using_otp)):
raise accounts_exceptions.InvalidLoginRequestError()
if login_using_password:
return Response(self._login_with_password(request))
elif login_using_otp:
return Response(self._login_with_otp(request))
raise accounts_exceptions.InvalidLoginRequestError()
return Response(self._login_with_password(request))
Also my _login_with_password looks like:
def _login_with_password(self, request, **kwargs):
"""
_login_with_password
A utility method to handle login with password
:param request:
:return:
"""
return getattr(ObtainJSONWebToken.as_view()(request=request._request, ), 'data')
When I try to login, Django complains saying RawPostDataException You cannot access body after reading from request's data stream
I'm using JWT to authenticate requests. ObtainJSONWebToken is a view provided by DRF-JWT to obtain access tokens to authenticate requests.
What is the workaround/solution for this?
Is there a better way to support such a login requirement?
Thanks in advance!
Resolved this.
There's no concrete way to solve the problem above.
Django disallows access to request.data multiple times.
It could be done only once for the entire request lifetime.
So, this left me with two solutions:
Move my request payload to query params.
Move my request payload to url context.
I ended up using a mix and match of both.
So, basically I used request.query_params and self.context to fetch data from the request and changed my URL and request structure accordingly.
I have scrached my head over this for almost 3-4 days.
Let me explain the situation. I have a DRF(Django REST Framework) class based view with a custom authentication class. As far as I understand, you can override the authenticate method of DRF's BaseAuthentication class to implement your custom authentication, while you can only raise predefined Exceptions provided from DRF if the authentication fails.
My problem is, I am trying to find a way to return custom response i.e; the captcha HTML to the frontend directly from the authentication class, so as to achieve no authentication related code in my view.
To have a better understanding of my situation I am providing a pseudo code below.
class ExampleView(APIView):
authentication_classes = (ExampleCustomAuth, )
def get(self, request):
pass
This is the view and this part is absolutely fine.
class ExampleCustomAuth(BaseAuthentication):
def authenticate(self, request):
req = request
request = req._request
{
This part of code decides if its required for a
captcha or not
}
if captcha_required:
response = HttpResponse()
response.status_code = 401
response['WWW-Authenticate'] = 'Captcha" id="%s"'% (id)
response.content = loader.render_to_string('captcha.html')
return response # This is where it goes wrong
I believe, its not possible to return a response right from here.
I hope someone has figured a way around this.
Thank you in advance !
Well I finally figured a way to get it working.
According to the DRF docs, the authenticate method should be overridden for any authentication logic and also have to override the authenticate_header, so that if you raise an exception in authenticate method, you can return a string from the authenticate_header method, which will be used as a value for the www-Authenticate header.
Below is how the implementation works.
class ExampleCustomAuth(BaseAuthentication):
def authenticate(self, request):
req = request
request = req._request
{
This part of code decides if its required for a
captcha or not
}
if captcha_required:
raise exceptions.AuthenticationFailed(loader.render_to_string('captcha.html'))
def authenticate_header(self, request):
return 'Captcha" id="%s"'% (id)
I am trying to decide whether I should use Django's Client or RequestFactory to test my views.
I am creating my server using DjangoRESTFramework and it's really simple, so far:
class SimpleModelList(generics.ListCreateAPIView):
"""
Retrieve list of all route_areas or create a new one.
"""
queryset = SimpleModel.objects.all()
serializer_class = SimpleModelSerializer
filter_backends = (IsOwnerFilterBackend,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
What are the differences between testing with Django's Client and RequestFactory and which approach is more suited for testing a REST server (if there is any difference besides liking one better)?
Should I create tests with both so as to provide a better coverage for my system?
RequestFactory and Client have some very different use-cases. To put it in a single sentence: RequestFactory returns a request, while Client returns a response.
The RequestFactory does what it says - it's a factory to create request objects. Nothing more, nothing less.
The Client is used to fake a complete request-response cycle. It will create a request object, which it then passes through a WSGI handler. This handler resolves the url, calls the appropriate middleware, and runs the view. It then returns the response object. It has the added benefit that it gathers a lot of extra data on the response object that is extremely useful for testing.
The RequestFactory doesn't actually touch any of your code, but the request object can be used to test parts of your code that require a valid request. The Client runs your views, so in order to test your views, you need to use the Client and inspect the response. Be sure to check out the documentation on the Client.
When using Django REST framework request factory would be helpfull to test the permissions.
EX:
Class TestPermission(TestCase):
def test_admin_permisiion(self):
admin_user = User.objects.create(email='admin#gmail.com',password='admin997',is_staff=True)
factory = RequestFactory()
request = factory.get('/')
request.user = admin_user
permission = IsAdminUser()
has_permission = permission.has_permission(request, None)
self.assertTrue(has_permission)
what we have done hear is we created a admin user by setting is_staff=True , then we created a request and assigned the admin as user of the request. request factory helps us do so. then we checked the IsAdminUser() permission from DRF against the request. the test will pass .
Client is to be used when you need to test the response returned by an Api.
In my Django application I want to keep track of whether a response has been sent to the client successfully. I am well aware that there is no "watertight" way in a connectionless protocol like HTTP to ensure the client has received (and displayed) a response, so this will not be mission-critical functionality, but still I want to do this at the latest possible time. The response will not be HTML so any callbacks from the client (using Javascript or IMG tags etc.) are not possible.
The "latest" hook I can find would be adding a custom middleware implementing process_response at the first position of the middleware list, but to my understanding this is executed before the actual response is constructed and sent to the client. Are there any hooks/events in Django to execute code after the response has been sent successfully?
The method I am going for at the moment uses a subclass of HttpResponse:
from django.template import loader
from django.http import HttpResponse
# use custom response class to override HttpResponse.close()
class LogSuccessResponse(HttpResponse):
def close(self):
super(LogSuccessResponse, self).close()
# do whatever you want, this is the last codepoint in request handling
if self.status_code == 200:
print('HttpResponse successful: %s' % self.status_code)
# this would be the view definition
def logging_view(request):
response = LogSuccessResponse('Hello World', mimetype='text/plain')
return response
By reading the Django code I am very much convinced that HttpResponse.close() is the latest point to inject code into the request handling. I am not sure if there really are error cases that are handled better by this method compared to the ones mentioned above, so I am leaving the question open for now.
The reasons I prefer this approach to the others mentioned in lazerscience's answer are that it can be set up in the view alone and does not require middleware to be installed. Using the request_finished signal, on the other hand, wouldn't allow me to access the response object.
If you need to do this a lot, a useful trick is to have a special response class like:
class ResponseThen(Response):
def __init__(self, data, then_callback, **kwargs):
super().__init__(data, **kwargs)
self.then_callback = then_callback
def close(self):
super().close()
self.then_callback()
def some_view(request):
# ...code to run before response is returned to client
def do_after():
# ...code to run *after* response is returned to client
return ResponseThen(some_data, do_after, status=status.HTTP_200_OK)
...helps if you want a quick/hacky "fire and forget" solution without bothering to integrate a proper task queue or split off a separate microservice from your app.
I suppose when talking about middleware you are thinking about the middleware's process_request method, but there's also a process_response method that is called when the HttpResponse object is returned. I guess that will be the latest moment where you can find a hook that you can use.
Furthermore there's also a request_finished signal being fired.
I modified Florian Ledermann's idea a little bit... So someone can just use the httpresponse function normally, but allows for them to define a function and bind it to that specific httpresponse.
old_response_close = HttpResponse.close
HttpResponse.func = None
def new_response_close(self):
old_response_close(self)
if self.func is not None:
self.func()
HttpResponse.close = new_response_close
It can be used via:
def myview():
def myfunc():
print("stuff to do")
resp = HttpResponse(status=200)
resp.func = myfunc
return resp
I was looking for a way to send a response, then execute some time consuming code after... but if I can get a background (most likely a celery) task to run, then it will have rendered this useless to me. I will just kick off the background task before the return statement. It should be asynchronous, so the response will be returned before the code is finished executing.
---EDIT---
I finally got celery to work with aws sqs. I basically posted a "how to". Check out my answer on this post:
Cannot start Celery Worker (Kombu.asynchronous.timer)
I found a filthy trick to do this by accessing a protected member in HttpResponse.
def some_view(request):
# ...code to run before response is returned to client
def do_after():
# ...code to run *after* response is returned to client
response = HttpResponse()
response._resource_closers.append(do_after)
return response
It works in Django 3.0.6 , check the "close" function in the prototype of HttpResponse.
def close(self):
for closer in self._resource_closers:
try:
closer()
except Exception:
pass
# Free resources that were still referenced.
self._resource_closers.clear()
self.closed = True
signals.request_finished.send(sender=self._handler_class)