I'm using a ModelViewSet with the IsAuthenticatedOrReadOnly permission class, like so:
class PostViewSet(viewsets.ModelViewSet, MarkdownToHTML):
permission_classes = (IsAuthenticatedOrReadOnly,)
When I call this view in the browsable API, the data returns in about 1100 ms (already too long), but when I call it from my frontend UI, the call takes 6000-7000ms!
The only difference between these two methods of calling the same view is that I am passing along a json token from my frontend UI app. When I comment out the token header, the response returns in about 1 second, the same time as in the browsable API.
How could this simple authentication step take over 5 seconds?
Here is the permission class:
class IsAuthenticatedOrReadOnly(BasePermission):
"""
The request is authenticated as a user, or is a read-only request.
"""
def has_permission(self, request, view):
return (
request.method in SAFE_METHODS or
request.user and
request.user.is_authenticated
)
I've run in a similar issue in a project. I would tell you about my experience in order to try to help, I can't tell what is your exact problem but I'll post things I checked when I had mine.
The thing is that decoding the auth token is a very expensive operation, so you have to check:
How many times is such token (if provided) decoded in your view?
Can you use cookies for caching auth token, setting an expire time?
How many time is this token send to and back from the server?
On the other hand, remember DRF will transform you json to a python object (specifically a dictionary) depending on the length of your token (and how many times it occurs) it will be a very expensive operation too.
Related
I am unable to send the user details along with requests module i had to hard code the user details in the data payload to identify the user.
full_url = ''.join(['http://', get_current_site(request).domain, '/am/reply'])
data = {
'agent_type':'trigger',
'input':platform,
'userid':request.user.id ####==>> had to send userid like this
}
a = requests.get(full_url,params=data)
Is there way to send all general request data using requests.?
And moreover the requests url the destination view i have implemented
def index(request):
if not request.user.is_authenticated:
return HttpResponseRedirect(reverse('login'))
And request.user.id is none when url is reached through requests module
In general how should i validate a request when using requests module
Django uses request and response objects to pass state through the system.
When a page is requested, Django creates an HttpRequest object that contains metadata about the request. Then Django loads the appropriate view, passing the HttpRequest as the first argument to the view function. Each view is responsible for returning an HttpResponse object.
Some of the middleware included in Django’s contrib apps set attributes on the request. If you don’t see the attribute on a request, be sure the appropriate middleware class like authenticationmiddleware,sessionmiddleware.
Following piece of code will give the user.id if and only if the user is authenticated.
def myview(request):
if request.user.is_authenticated:
print request.user.id
else:
... # Do something else.
https://docs.djangoproject.com/en/1.10/ref/request-response/
If I understood your question correctly, You are getting request in one view, and then making a call to other view using requests module. It that case the request object in index view will be totally different because that request was sent from your server where application works, not from user. You can only get data in index view using request.GET.get("userid") and so on. And then if you will need user info, just fetch it again from database using userid. Passing request object to other view using requests library is not possible.
I am using the Django rest framework JSON Web token API that is found here on github (https://github.com/GetBlimp/django-rest-framework-jwt/tree/master/).
I can successfully create tokens and use them to call protected REST APis. However, there are certain cases where I would like to delete a specific token before its expiry time. So I thought to do this with a view like:
class Logout(APIView):
permission_classes = (IsAuthenticated, )
authentication_classes = (JSONWebTokenAuthentication, )
def post(self, request):
# simply delete the token to force a login
request.auth.delete() # This will not work
return Response(status=status.HTTP_200_OK)
The request.auth is simply a string object. So, this is of course, not going to work but I was not sure how I can clear the underlying token.
EDIT
Reading more about this, it seems that I do not need to do anything as nothing is ever stored on the server side with JWT. So just closing the application and regenerating the token on the next login is enough. Is that correct?
The biggest disadvantage of JWT is that because the server does not save the session state, it is not possible to abolish a token or change the token's permissions during use. That is, once the JWT is signed, it will remain in effect until it expires, unless the server deploys additional logic.
So, you cannot invalidate the token even you create a new token or refresh it. Simply way to logout is remove the token from the client.
Yes, it's correct to say that JWT tokens are not stored in the database. What you want, though, is to invalidate a token based on user activity, which doesn't seem to be possible ATM.
So, you can do what you suggested in your question, or redirect the user to some token refreshing endpoint, or even manually create a new token.
Add this in Admin.py
class OutstandingTokenAdmin(token_blacklist.admin.OutstandingTokenAdmin):
def has_delete_permission(self, *args, **kwargs):
return True # or whatever logic you want
admin.site.unregister(token_blacklist.models.OutstandingToken)
admin.site.register(token_blacklist.models.OutstandingToken, OutstandingTokenAdmin)
from rest_framework_simplejwt.token_blacklist.admin import OutstandingTokenAdmin
from rest_framework_simplejwt.token_blacklist.models import OutstandingToken
class OutstandingTokenAdmin(OutstandingTokenAdmin):
def has_delete_permission(self, *args, **kwargs):
return True # or whatever logic you want
def get_actions(self, request):
actions = super(OutstandingTokenAdmin, self).get_actions(request)
if 'delete_selected' in actions:
del actions['delete_selected']
return actions
admin.site.unregister(OutstandingToken)
admin.site.register(OutstandingToken, OutstandingTokenAdmin)
I am currently using code found here:
http://flask.pocoo.org/snippets/8/
And I decorate my function accordingly to have the admin authenticate when requesting a specific admin page. However, instead of requiring the admin to keep authenticating each time they admin page, I noticed that it somehow keeps track of the session and no longer requires authentication after successfully authenticating once. Is there some way to force flask to re-authenticate every time an admin requests the given decorated admin page?
Using the included snippet, there is no good way to force a user to log in every time they request the given page.
This is because that snippet is using HTTP Basic Auth and there is no good way to ask the browser to stop sending that header.
What you are looking for can be done with a custom decorator. You can use the sample below. Note that your case will be different, but you can use this as a guide.
from web import app, has_role
#app.route("/admin/my_page")
#login_required
#has_role(role="admin")
def admin_my_page():
//do stuff
Then, in your project init, or an include file you can add the following:
def has_role(role=None):
def _initial_decorator(view_func):
def _decorator(*args, **kwargs):
response = view_func(*args, **kwargs)
if g.user.user_level != role:
from flask import redirect, url_for
return redirect(url_for("no_access"))
return response
return wraps(view_func)(_decorator)
return _initial_decorator
This should at lease give you an idea of how to create a custom decorator, and then check for role permissions. You can expand this to however you need. You can put engine logic, or other checks to fit your project.
I am trying to figure out the best way to implement token based authentication in my django app. An external, non-django application is setting a cookie, with a token, and I have a webservice that can retrieve user information based off of that token. If the user has the cookie set, they should not need to authenticate on my site and should be automatically logged in based on the info passed back by the web service. As I see it, there are a few different options to perform the actual check and I'm not sure which is best:
Write a custom decorator like the one in this snippet and use it instead of
login_required.
Call a custom authenticate method inside base_site through an ajax call. On every page, a check would be made and if the cookie exists and is valid, then the user would be automatically logged in.
Add some javascript to the LOGIN_REDIRECT_URL page that will check/validate the cookie in an ajax call, and automatically redirect back to the referrer if the cookie authenticated.
Is there an option I am missing? Ideally, there would be a way to build this into login_required, without having to write a custom decorator.
Before searching for code, be sure you read the documentation. http://docs.djangoproject.com/en/1.2/topics/auth/#other-authentication-sources
Also read the supplied Django source.
You want to create three things.
Middleware to capture the token. This is where most of the work happens. It checks for the token, authenticates it (by confirming it with the identity manager) and then logs in the user.
Authentication backend to find Users. This is a stub. All it does is create users as needed. Your identity manager has the details. You're just caching the current version of the user on Django's local DB.
Here's the middleware (edited).
from django.contrib.auth import authenticate, login
class CookieMiddleware( object ):
"""Authentication Middleware for OpenAM using a cookie with a token.
Backend will get user.
"""
def process_request(self, request):
if not hasattr(request, 'user'):
raise ImproperlyConfigured()
if "thecookiename" not in request.COOKIES:
return
token= request.COOKIES["thecookiename"]
# REST request to OpenAM server for user attributes.
token, attribute, role = identity_manager.get_attributes( token )
user = authenticate(remote_user=attribute['uid'][0])
request.user = user
login(request, user)
The identity_manager.get_attributes is a separate class we wrote to validate the token and get details on the user from the IM source. This, of course, has to be mocked for testing purposes.
Here's a backend (edited)
class Backend( RemoteUserBackend ):
def authenticate(**credentials):
"""We could authenticate the token by checking with OpenAM
Server. We don't do that here, instead we trust the middleware to do it.
"""
try:
user= User.objects.get(username=credentials['remote_user'])
except User.DoesNotExist:
user= User.objects.create(username=credentials['remote_user'] )
# Here is a good place to map roles to Django Group instances or other features.
return user
This does not materially change the decorators for authentication or authorization.
To make sure of this, we actually refresh the User and Group information from our
identity manager.
Note that the middleware runs for every single request. Sometimes, it's okay to pass the token to the backed authenticate method. If the token exists in the local user DB, the request can proceed without contacting the identity manager.
We, however, have complex rules and timeouts in the identity manager, so we have to examine every token to be sure it's valid. Once the middleware is sure the token is valid, we can then allow the backend to do any additional processing.
This isn't our live code (it's a little too complex to make a good example.)
I'm building a Django application with Django-Rest-Framework APIs. I have built an API endpoint as shown below.
I want to be able to POST data from my browser. I want this POST operation to retrieve an object model from my Database that has the matching primary key as given in the URL. And I want to modify that retrieved object based on the data posted by the browser.
If I could just grab the posted data from with my ViewSet, I would be done. But when I try to execute that viewset's update() function, I get a CSRF error.
From my urls.py file:
router.register(r'replyComment', views.ReplyComment, base_name="replyComment")
From my views.py file:
class ReplyComment(viewsets.ViewSet):
def update(self,request,pk=None):
try:
origComment = Comment.objects.get(pk=pk)
# Do something here that modifies the state of origComment and saves it.
return Response(
json.dumps(True),
status=status.HTTP_200_OK,
)
except Exception as exception:
logger.error(exception)
return Response(status=status.HTTP_400_BAD_REQUEST)
I'm using the Advanced Rest Client (ARC) tool in my Chrome browser. When I point the ARC tool to http://127.0.0.1:3001/api/replyComment/2/ using the POST method, I get the following error:
{
detail: "CSRF Failed: CSRF token missing or incorrect".
}
This doc indicates that I should use the #csrf_exempt decorator. I put that decorator on my update() function above. But it seemed to make no difference.
What changes do I need to make to ensure my POST works as I intend it to?
It is highly recommended NOT to disable CSRF protection for session authentication. Doing so will make your app vulnerable to attacks. For the API, DRF enforces CSRF protection only for session authentication. If you use another authentication backend(Basic, Auth Token or OAuth) it will work with out asking for CSRF tokens since CSRF attacks happen only in browsers. Now if your API is going to be used by non-browser clients, you can enable one of the other auth backends. For example, Using Basic auth:
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
),
And enable basic auth in ARC.