In my website, people can see images given from REST APIs without login. I want to make some of my APIs not to require a user token. So, I had some research and I found the way below.
Since I need to use this way for production, I want to make sure if it is a safe way. The stores that will be given from the API have just store data such as name, urls, description, and images. Any of them are not related to any users.
Is it okay for me to use in production? Just so you know, I use Django REST Framework to serve data to frontend and use React js to show them in frontend side.
from rest_framework.decorators import authentication_classes, permission_classes
#authentication_classes([])
#permission_classes([])
class ListAllStores(APIView):
def get(self, request, format=None):
all_stores = Store.objects.all()
serializer = StoreSerializer(all_stores, many=True)
return Response(data=serializer.data)
You can try something link sending user type on header of each request.
And for the API without token send userType as anonymous or something like that
And for the API with token send userType as customer or something like that.
Create a method which will be called first thing from the each end points and validate the Request header.
If you want to make it more general you can map this end point and type of user as meta data setting, So next time if you have some '/image' end point which was allowed only to the customer user type but now you want it should be allowed to anon also so that can be easily done using that meta data file without changing the code.
You can be explicit by using AllowAny permission class, which will let public access to that endpoint.
from rest_framework import permissions, views
class ListAllStores(views.APIView):
permission_classes = (
permissions.AllowAny,
)
def get(self, request, format=None):
all_stores = Store.objects.all()
serializer = StoreSerializer(all_stores, many=True)
return Response(data=serializer.data)
Related
I am new to Django Rest Framework and working on a DRF & React Authentication system. Everything is working fine except password reset. The issue I have is when the user requests a password reset a reset URL with the generated token is fired to the registered email. So the user can click the link with the embedded token and go back to the calling (or any other) URL.
http://127.0.0.1:8000/api/auth/password_reset/validate_token/?token=wBBAIfQHzTJVz9bKPLkIgB
The problem is how do I generate the view or serializer to handle the URL with the token on the fly. I just need to understand the full flow I am working with "django-rest-passwordreset1.2.1", (https://pypi.org/project/django-rest-passwordreset/) package so I am generating the reset email and token with the model which as per documentation fires a signal to generate the token and send an email. The endpoints that come with the documentation validate and work fine in postman testing. I am just not sure how to create that functionality in Django. Any help or links to a good tutorial will be appreciated.
Many thanks in advance
models.py
from django.dispatch import receiver
from django.urls import reverse
from django_rest_passwordreset.signals import reset_password_token_created
from django.core.mail import send_mail
#receiver(reset_password_token_created)
def password_reset_token_created(sender, instance, reset_password_token, *args,
**kwargs):
site = 'http://127.0.0.1:3000'
email_plaintext_message = "{}?token={}".format(reverse('password_reset:reset-password-validate'), reset_password_token.key)
reset_url = site + email_plaintext_message
send_mail(
"Password Reset for {title}".format(title="Some website title"),
reset_url,
"noreply#somehost.local",
[reset_password_token.user.email]
)
urls.py
urlpatterns = [
path('api/auth', include('knox.urls')),
path('api/auth/register', RegisterAPI.as_view()),
path('api/auth/login', LoginAPI.as_view()),
path('api/auth/user', UserAPI.as_view()),
path('api/auth/logout', knox_views.LogoutView.as_view(), name="knox_logout"),
path('api/auth/change-password/', ChangePasswordView.as_view(), name='change-password')
path('api/auth/password_reset/', include('django_rest_passwordreset.urls')),
path('admin/', admin.site.urls),
]
The include('django_rest_passwordreset.urls')) provides the following url's:
POST ${API_URL}/ -
request a reset password token by using the email parameter*
POST ${API_URL}/confirm/ -
using a valid token, the users password is set to the provided password
POST ${API_URL}/validate_token/ -
will return a 200 if a given token is valid
where ${API_URL}/ is the url specified in your urls.py (e.g., api/auth/password_reset/ as in the example above)*
OK, let's abstract from libs, and get proper steps to make it manually.
As far as I've got, you have some token token=wBBAIfQHzTJVz9bKPLkIgB that somehow linked with the User instance, am I right?
So we need to write a view that allows us to make auth and get a request with our User and then we can check the token and make a logout.
from django.contrib.auth import logout
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication
#api_view["GET"]
#authentication_classes([TokenAuthentication])
#permission_classes([IsAuthenticated])
def logout(request):
if request.query_params.get("token"):
if Token.objects.filter(token=request.query_params["token"]).exists()
logout(request)
# other logic is here
return Response({"status": "logged out"})
return Response({"error": "..."})
Point your urls.py file to use this API view in the needed path.
So my assumption here is that you are using something like TokenAuthentication you can change the decorator's parameter to your actual value. The same thing is for the permission_classes decorator.
The problem was with the front end, I was able to modify the parameters in the model that generated and stored the token by replacing the URL. I changed
email_plaintext_message = "{}?token={}".format(reverse('password_reset:reset-password-validate'), reset_password_token.key)
to the following
email_plaintext_message = "{}?token={}".format('http://127.0.0.1:3000/newpassword' , reset_password_token.key)
Now the return URL is http://127.0.0.1:3000/newpassword?token=f109f93a83040895e3a8b1b8afd
pointing to the correct component in the fornt-end. I then extract the token from the URL and pass it for validation along with the new password in the form hitting the required endpoint.
I am using Django REST Framework (DRF) for my API. In particular, I have set up several routes using the ModelViewSet, without much additional coding. In particular, I have a route like
/api/v1/collection/<key>
From all the possible keys, I do need to block one specific key, say special. Any request with any HTTP verb to /api/v1/collection/special should result in a HTTP 404 error.
I can think of two approaches:
Override some method on the ViewSet hierarchy. Which one?
In urls.py, set up a higher-precedence URL route that intercepts this URL, like path("collection/special", view=<404View>, name="collection-exception"). Does this make sense? What would be the appropriate exception view to route to?
What is the recommended approach? Any of the above, or something else?
If you are using the ModelViewSet then you should overwrite the get_queryset() method. If key value is wrong then raise the Exception otherwise return the queryset:
from rest_framework.exceptions import NotFound
class MyModelViewSet(viewsets.ModelViewSet):
# ...
def get_queryset(self):
key = self.kwargs.get("key")
if key == "special":
raise NotFound()
return MyModel.objecs.filter(pk=key)
Hello I have an application where the user access a login in and thereby obtains a token through an endpoint.
I want to block certain users according to their profile (which is linked to a company).
What I did was put my logic here:
class CustomTokenAuthenticationView(ObtainAuthToken):
def post(self, request, *args, **kwargs):
...
I can do it, but I wanted to do something more hard. In this case I wanted to block access to the token as a whole. What would be the best way?
First of all, I hope the title of this question is clear enough. The following lines will make things clearer if it is not the case.
I have different users in my database. Some are part of the staff and some are just regular users.
In my API, I have a /users route. My idea is that when a staff member requests it (GET), he will see only the staff users and when a regular user will request it, he will only see the regular users.
Whether or not the user which makes the request is a member of the staff or not is an information which is stored in the JWT token.
Therefore, I used the following code:
class CustomUserList(generics.ListCreateAPIView):
serializer_class = CustomUserSerializer
def get_queryset(self):
token = self.request.META.get('HTTP_AUTHORIZATION')[7:] # [7:] to get rid of 'Bearer '
is_staff = jwt.decode(token, None, None)['is_staff']
print(is_staff)
queryset = CustomUser.objects.filter(is_staff=is_staff)
return queryset
This code works but is there a more direct / logical way to accomplish this ?
It feels a bit off storing what is essentially a server-side permission/setting on the client side (even though some people do use JWT for that). What if a user is promoted to/demoted from staff? You'd have to revoke all such client tokens? Also, what if you need to attach more conditions to these queries?
I think a slightly more flexible approach is to store some kind of a user id in the JWT token, then use a TokenAuthentication class with JWT to establish the identity of the user, attach it to something like request.user and then in your get_queryset method filter by request.user.is_staff. That way, you can attach any context/filters/permissions to the authenticated users on the server side and don't need to rely on the client to present explicit claim(s) to filter out the objects they can access. You'd end up with an extra call to the database to populate request.user, but you might end up needing that anyway.
Authenticating with a JWT token in the header retrieves the user so your view will have access to request.user and all of the user's attributes in every method after the dispatch() method (the first method that is run after .as_view() is triggered when sending a request to an endpoint).
If you're using djangorestframework-simplejwt, the DRF recommended JWT package, all you need to do is this:
class CustomUserList(generics.ListCreateAPIView):
queryset = CustomUser.objects.all()
serializer_class = CustomUserSerializer
def get_queryset(self):
queryset = super().get_queryset()
if self.request.user.is_staff:
queryset = queryset.filter(is_staff=True)
return queryset
I have a model say TestModel as follows:
class TestModel(models.Model):
name = models.CharField()
description = models.TextField()
Now I can use a ModelForm to save data to this model. However, say I want to use Ajax and send a url as follows savethis?name=ABC&desc=SomeRandomDescription to a view that handles it as follows:
def savethis(request):
if request.GET['name'] and request.GET['desc']:
name = request.GET['name']
desc = request.GET['desc']
test = TestModel(name=name, description=desc)
test.save
return HttpResponse('Ok')
else:
return HttpResponse('Fail')
What's to stop someone from running a script that can easily hit this url with valid data and thus save data to my model? How do I ensure that incoming data is sent only from the right source?
One option is sending the data as JSON in a Post request but even that's not too hard to emualte.
Seems that you have stumbled upon the great security flaw that is Cross-site Scripting attacks. They are several ways you can get around it, but going into all of them in one answer would be fruitless. I suggest you Google the term and do some poking around, and you will find several different methods on how to protect your site better.
Django has a security page dedicated to talking about how to protect your site.