I am building a solution where I have one core API on which I have implemented the throttling as per the official documentation https://www.django-rest-framework.org/api-guide/throttling/. But I wonder how will I be able to monitor the requests so that no genuine user of the app gets blocked and if so should be able to unblock it.
My settings.py file
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.ScopedRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'students': '1/minute',
}
}
And My views.py
class StudentViewSet(viewsets.ModelViewSet):
throttle_scope = 'students'
The throttle classes provided by Django REST framework do not allow you to do this. You would have to create a custom throttle class and overwrite allow_request() to log throttling events and provide some facility for whitelisting. E.g. something like this:
class WhitelistScopedRateThrottle(throttling.ScopedRateThrottle):
def allow_request(self, request, view):
allowed = super().allow_request(request, view)
if not allowed:
if self.is_whitelisted(request, view)
return True
else:
self.log_throttling(request, view)
return False
else:
return True
def is_whitelisted(self, request, view):
...
def log_throttling(self, request, view):
...
How to best implement the whitelisting and logging depends on your exact requirements.
Related
I have three APIs created by inheriting APIView class
class API1View(APIView):
def get(self, request):
return Response("API 1 view")
class API2View(APIView):
def get(self, request):
return Response("API 2 view")
class API3View(APIView):
def get(self, request):
return Response("API 3 view")
I have three users
user1
user2
user3
I three kind of users.
customer
manager
employee
Different kind of users should have access to different APIs
The Manager can only able to view the API1 and API2
The Customer can only able to view the API1
The Employee can only able to view the API3
In future, I need to able to revoke access of a certain user to certain API.
Is it good to use Django Groups or is there any better ways to do it?
Or is there any DRF way to do it? (I have seen DRF permissions but I'm not sure if that will work for this scenario.)
You can create your custom permissions and use them in your APIs:
from rest_framework.permissions import BasePermission
class CustomerPermission(BasePermission):
message = 'You Don\'t Have Access To Customer APIs'
def has_permission(self, request, view):
return bool(request.user.is_customer)
Usage:
class API3View(APIView):
permission_classes = [CustomerPermission]
def get(self, request):
return Response("API 3 view")
The permission_classes gets multiple permissions but it works like
an and if you want to set access to an API for multiple users, you
should write custom permission for it too
Our app is scaling and is lagging due to uploads not being done through signed urls.
Will the following code still block the event loop if a user tries to POST a large file to this endpoint? Or will it be rejected, allowing our event loop to continue?
If this does still block the event loop, what's a better solution to temporarily turn the endpoints off? Is a 405 from DRF a viable way to do this by turning off the method? We prefer a 400 so we can return a custom error.
class UploadViewset(
mixins.CreateModelMixin, viewsets.GenericViewSet
):
permission_classes = [permissions.IsAuthenticated]
def create(self, request, room_pk=None):
########################
# Temporarily Disable
########################
return Response(
{
"detail": "Uploads have been temporarily disabled for performance."
},
status=status.HTTP_400_BAD_REQUEST,
)
I am trying to build a system in Django without using any of its batteries -- auth, admin etc. The system uses the Django rest framework for the API.
However, when I try to request to an API, I keep getting this error:
Model class django.contrib.auth.models.Permission doesn't declare an explicit
app_label and isn't in an application in INSTALLED_APPS.
I do not want to use django.contrib.auth at all. I did the following inside my DRF API View class:
class NewsPostView(APIView):
permission_classes = None
def get(self, request, format=None):
posts = NewsPost.objects.all()
serializer = NewsPostSerializer(posts, many=True)
return Response([])
However, I am still getting the same error. How can I disable auth from DRF?
I have solved my issue. After #Linovia's response, I checked the docs etc of DRF and changed the following properties:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [],
'DEFAULT_PERMISSION_CLASSES': [],
'UNAUTHENTICATED_USER': None,
}
And everything worked.
Use AllowAny instead of None. And also your response seems to be returning empty list. use serializer.data for retrieving data
from rest_framework.permissions import AllowAny
class NewsPostView(APIView):
permission_classes = (AllowAny,)
def get(self, request, format=None):
posts = NewsPost.objects.all()
serializer = NewsPostSerializer(posts, many=True)
return Response(data=serializer.data)
Make sure you don't use rest_framework.urls and that your settings has:
'DEFAULT_AUTHENTICATION_CLASSES': tuple(),
as well as your views.
With some luck, you won't have the authentication imported through another import.
I want to disable OPTIONS method on my API built with Django Rest Framework (DRF) globally (on all the API endpoints)
currently an OPTIONS call returns,
{
"parses": [
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
],
"renders": [
"application/json"
],
"name": "Login Oauth2",
"description": ""
}
which is something that I don't want someone to peek into. I want to return a blank character as github does on its API or something else.
I tried
#api_view(['POST'])
def my_method(request):
if request.method == 'OPTIONS':
return Response()
on an function based view, which returns an but inspecting the headers show,
Allow →POST, OPTIONS, OPTIONS
which has a repeated OPTIONS.
How do I achieve it? Thanks.
In your settings, add something like:
REST_FRAMEWORK = {
'DEFAULT_METADATA_CLASS': None,
}
if DEBUG:
# the default value
REST_FRAMEWORK['DEFAULT_METADATA_CLASS']: 'rest_framework.metadata.SimpleMetadata'
You can implement your own metadata class as well. Setting this to None will make it return HTTP 405 on OPTIONS requests.
Just implement a custom permission class.
your_app/permissions.py (permissions file for your app)
from rest_framework import permissions
class DisableOptionsPermission(permissions.BasePermission):
"""
Global permission to disallow all requests for method OPTIONS.
"""
def has_permission(self, request, view):
if request.method == 'OPTIONS':
return False
return True
Also set this as the default permission globally, using the DEFAULT_PERMISSION_CLASSES setting.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'your_app_name.permissions.DisableOptionsPermission',
)
}
This will disallow all incoming requests for OPTIONS method.
I'm using django-allauth on my website for social logins. I also have a REST API powered by django-rest-framework that serves as the backend of a mobile app. Is there a way I can directly plug in allauth's authentication backend to the REST api so that I can validate (and register) users who use Facebook login in the mobile app?
To clarify: The Facebook login part is handled by native SDKs. I need an endpoint that works like POST /user (that is, creates a new user), but takes Facebook oauth token as input instead of email/password etc.
You can use Django Rest Auth for this which depends on django-allauth. It's very easy to integrate.
You can use this libray for social authentication django-rest-framework-social-oauth2. Try this django-allauth related code
urls.py
urlpatterns = [
url(
r'^rest/facebook-login/$',
csrf_exempt(RestFacebookLogin.as_view()),
name='rest-facebook-login'
),
]
serializers.py
class EverybodyCanAuthentication(SessionAuthentication):
def authenticate(self, request):
return None
views.py
class RestFacebookLogin(APIView):
"""
Login or register a user based on an authentication token coming
from Facebook.
Returns user data including session id.
"""
# this is a public api!!!
permission_classes = (AllowAny,)
authentication_classes = (EverybodyCanAuthentication,)
def dispatch(self, *args, **kwargs):
return super(RestFacebookLogin, self).dispatch(*args, **kwargs)
def get(self, request, *args, **kwargs):
try:
original_request = request._request
auth_token = request.GET.get('auth_token', '')
# Find the token matching the passed Auth token
app = SocialApp.objects.get(provider='facebook')
fb_auth_token = SocialToken(app=app, token=auth_token)
# check token against facebook
login = fb_complete_login(original_request, app, fb_auth_token)
login.token = fb_auth_token
login.state = SocialLogin.state_from_request(original_request)
# add or update the user into users table
complete_social_login(original_request, login)
# Create or fetch the session id for this user
token, _ = Token.objects.get_or_create(user=original_request.user)
# if we get here we've succeeded
data = {
'username': original_request.user.username,
'objectId': original_request.user.pk,
'firstName': original_request.user.first_name,
'lastName': original_request.user.last_name,
'sessionToken': token.key,
'email': original_request.user.email,
}
return Response(
status=200,
data=data
)
except:
return Response(status=401, data={
'detail': 'Bad Access Token',
})
While I'm not quite sure how to use allauth and rest-fremework together, allauth does not offer such an endpoint.
Suggestion: make your own that does a variation of the following:
Call allauth.socialaccount.providers.facebook.views.fb_complete_login(None, socialtoken) where socialtoken is as created in login_by_token. That performs (a few functions deeper) a django.contrib.auth.login, possibly creating the acct.
After that, for use on mobile devices, it might be possible to the the auth (not FB) token: get the user data (from session?), and call rest_framework.authtoken.views.obtain_auth_token
Notes:
1. This offers no way to resolve email conflicts or connect social/local accts.
2. I haven't tried it - please post code if you can get it working.
You could use djoser but I don't know how it cooperates with allauth:
https://github.com/sunscrapers/djoser