Manipulate URL fragment on redirect in Django - python

I have been using Django's auth module but recently had to switch off of it. The auth module handled session expirations by redirecting to the login page and setting the page you were logged out from as the ?next=/currentPage parameter.
So if my session expired on #dashboard I would be redirected to:
mydomain/account/login?next=/#dashboard
After moving off of Django auth module, I noticed this has changed to:
mydomain/account/login/#dashboard
This seems consistent with how I understand it should function after reading URL Fragment and 302 redirects, however, I'd like to manipulate this fragment but don't see it anywhere in my HttpResponse object.
django.contrib.auth uses the following functions to handle this request, but I don't see where they're moving the fragment. Realistically I'd just like to remove the fragment completely, but if I can't I'd settle for having it back in ?next= parameter.
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
"""
Decorator for views that checks that the user passes the given test,
redirecting to the log-in page if necessary. The test should be a callable
that takes the user object and returns True if the user passes.
"""
def decorator(view_func):
#wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
if test_func(request.user):
return view_func(request, *args, **kwargs)
path = request.build_absolute_uri()
# urlparse chokes on lazy objects in Python 3, force to str
resolved_login_url = force_str(
resolve_url(login_url or settings.LOGIN_URL))
# If the login url is the same scheme and net location then just
# use the path as the "next" url.
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
(not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(
path, resolved_login_url, redirect_field_name)
return _wrapped_view
return decorator
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
"""
Decorator for views that checks that the user is logged in, redirecting
to the log-in page if necessary.
"""
actual_decorator = user_passes_test(
lambda u: u.is_authenticated(),
login_url=login_url,
redirect_field_name=redirect_field_name
)
if function:
return actual_decorator(function)
return actual_decorator

They're not removing it. Fragments are never sent to the backend: that's just how browsers work.
Note that your original URL is not being interpreted as next="/#dashboard" at all, but next="/", with a subsequent fragment of "dashboard". That is, the fragment is separate to the querystring, not part of it.
What you need to do is to encode the hash, so that it is part of the querystring itself.
mydomain/account/login?next=/%23dashboard

Related

django-csrf protection on ajax post

I am working on django 1.7 and python 2.7. To prevent csrf attack on ajax post request i have added csrf token in ajax header before send.
In views i have added csrf_protect decorator to check the token. Everything is working fine in this scenario. But In my project this function in views.py is being called internally in other views where i haven't implemented csrf token, this causing 403 error.So what i want to do is only when there is ajax post call have to check csrf_protect decorator. Every other calls can neglect.
def check_token(func):
def wrapper(request, *args, **kwargs):
if request.is_ajax():
return csrf_protect(func(request,*args, **kwargs))
return func(request,*args, **kwargs )
return wrapper
#check_token
def myViews(request,mob,id):
"""function starts here"""
Your decorator is equivalent to
myViews = check_token(myViews)
You could apply it manually to a new name:
def unprotected_view(request,mob,id):
"""function starts here"""
...
protected_view = check_token(unprotected_view)
Now you have a decorated and a non-decorated name for it.
Calling views from within other views is not really what Django views are supposed to do. If you have some generic functionality to craft a response, make that a separate function. Then let your protected view call that function and your other views call it as well:
#csrf_protect
def my_view(request, mob, id):
return craft_generic_response(request, mob, id)
def craft_generic_response(request, mob, id)
# do stuff to create response
return response
def another_view(request, mob, id):
# do stuff
response = craft_generic_response(**kwargs)
# do more stuff

twisted render_GET redirect to function

I can't seem to find any documentation on how to use twisted.web.util.Redirect to redirect to another function.
For more information see below.
I have the following code:
class Login(Resource):
isLeaf = True
def getChild(self, name, request):
if name == '':
return self
return Resource.getChild(self, name, request)
def render_GET(self, request):
saml = SamlRequest(request)
print('redirecting to sso')
return Redirect(saml.sso())
class SamlRequest(object):
self.auth = OneLogin_Saml2_Auth(self.request, custom_base_path=settings_path)
def sso(self):
return self.auth.login()
I need to redirect to page login to the login function inside OneLogin_Saml2_Auth.
When I try to redirect as-is I receive the error
raise TypeError("Unicode object not allowed as URL")
Is there a way to accomplish this?
twisted.web.util.Redirect is a kind of Resource, not something you return from a render method. It's most suitable if you have a "static" redirect that exists in your URL structure and redirects to a fixed location.
If you want to write a custom resource that redirects to a dynamic URL, use twisted.web.util.redirectTo(url, request), which you can call from your render_GET method just like you tried to do with Redirect.

Get the Flask view function that matches a url

I have some url paths and want to check if they point to a url rule in my Flask app. How can I check this using Flask?
from flask import Flask, json, request, Response
app = Flask('simple_app')
#app.route('/foo/<bar_id>', methods=['GET'])
def foo_bar_id(bar_id):
if request.method == 'GET':
return Response(json.dumps({'foo': bar_id}), status=200)
#app.route('/bar', methods=['GET'])
def bar():
if request.method == 'GET':
return Response(json.dumps(['bar']), status=200)
test_route_a = '/foo/1' # return foo_bar_id function
test_route_b = '/bar' # return bar function
app.url_map stores the object that maps and matches rules with endpoints. app.view_functions maps endpoints to view functions.
Call match to match a url to an endpoint and values. It will raise 404 if the route is not found, and 405 if the wrong method is specified. You'll need the method as well as the url to match.
Redirects are treated as exceptions, you'll need to catch and test these recursively to find the view function.
It's possible to add rules that don't map to views, you'll need to catch KeyError when looking up the view.
from werkzeug.routing import RequestRedirect, MethodNotAllowed, NotFound
def get_view_function(url, method='GET'):
"""Match a url and return the view and arguments
it will be called with, or None if there is no view.
"""
adapter = app.url_map.bind('localhost')
try:
match = adapter.match(url, method=method)
except RequestRedirect as e:
# recursively match redirects
return get_view_function(e.new_url, method)
except (MethodNotAllowed, NotFound):
# no match
return None
try:
# return the view function and arguments
return app.view_functions[match[0]], match[1]
except KeyError:
# no view is associated with the endpoint
return None
There are many more options that can be passed to bind to effect how matches are made, see the docs for details.
The view function can also raise 404 (or other) errors, so this only guarantees that a url will match a view, not that the view returns a 200 response.
In addition to the #davidism answer (The core developer of flask).
Note that if you want to discover the view function of the current url processed by flask app. You can use the Request object of flask which :
The request object used by default in Flask. Remembers the
matched endpoint and view arguments.
and the Flask.view_functtions which:
#: A dictionary mapping endpoint names to view functions.
#: To register a view function, use the :meth:route decorator.
def get_view_function():
if request.url_rule:
return current_app.view_functions.get(request.url_rule.endpoint, None)

Django and Middleware which uses request.user is always Anonymous

I'm trying to make middleware which alters some fields for the user based on subdomain, etc...
The only problem is the request.user always comes in as AnonymousUser within the middleware, but is then the correct user within the views. I've left the default authentication and session middleware django uses within the settings.
There is a similar question here: Django, request.user is always Anonymous User
But doesn't overly answer the total question because I'm not using different authentication methods, and djangos authentication is running before I invoke my own middleware.
Is there a way, while using DRF, to get the request.user within the middleware? I'll show some sample code here:
class SampleMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
#This will be AnonymousUser. I need it to be the actual user making the request.
print (request.user)
def process_response(self, request, response):
return response
with process_request:
class SampleMiddleware(object):
def process_request(self, request):
#This will be AnonymousUser. I need it to be the actual user making the request.
print (request.user)
def process_response(self, request, response):
return response
I've solved this problem by getting DRF token from the requests and loading request.user to the user associated to that model.
I had the default django authentication and session middleware, but it seems DRF was using it's token auth after middleware to resolve the user (All requests were CORS requests, this might have been why). Here's my updated middleware class:
from re import sub
from rest_framework.authtoken.models import Token
from core.models import OrganizationRole, Organization, User
class OrganizationMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
header_token = request.META.get('HTTP_AUTHORIZATION', None)
if header_token is not None:
try:
token = sub('Token ', '', header_token)
token_obj = Token.objects.get(key = token)
request.user = token_obj.user
except Token.DoesNotExist:
pass
#This is now the correct user
print (request.user)
This can be used on process_view or process_request as well.
Hopefully this can help someone out in the future.
Came across this today while having the same problem.
TL;DR;
Skip below for code example
Explanation
Thing is DRF have their own flow of things, right in the middle of the django request life-cycle.
So if the normal middleware flow is :
request_middleware (before starting to work on the request)
view_middleware (before calling the view)
template_middleware (before render)
response_middleware (before final response)
DRF code, overrides the default django view code, and executes their own code.
In the above link, you can see that they wrap the original request with their own methods, where one of those methods is DRF authentication.
So back to your question, this is the reason using request.user in a middleware is premature, as it only gets it's value after view_middleware** executes.
The solution I went with, is having my middleware set a LazyObject.
This helps, because my code (the actual DRF ApiVIew) executes when the actual user is already set by DRF's authentication.
This solution was proposed here together with a discussion.
Might have been better if DRF had a better way to extend their functionality, but as things are, this seems better than the provided solution (both performance and readability wise).
Code Example
from django.utils.functional import SimpleLazyObject
def get_actual_value(request):
if request.user is None:
return None
return request.user #here should have value, so any code using request.user will work
class MyCustomMiddleware(object):
def process_request(self, request):
request.custom_prop = SimpleLazyObject(lambda: get_actual_value(request))
The accepted answer only takes TokenAuthentication in consideration - in my case, there are more authentication methods configured. I thus went with initializing the DRF's Request directly which invokes DRF's authentication machinery and loops through all configured authentication methods.
Unfortunately, it still introduces an additional load on the database since the Token object must be queried (the accepted answer has this problem as well). The trick with SimpleLazyObject in this answer is a much better solution, but it didn't work for my use case because I need the user info in the middleware directly - I'm extending the metrics in django_prometheus and it processes the request before get_response is called.
from rest_framework.request import Request as RestFrameworkRequest
from rest_framework.views import APIView
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
drf_request: RestFrameworkRequest = APIView().initialize_request(request)
user = drf_request.user
...
return self.get_response(request)
Based on Daniel Dubovski's very elegant solution above, here's an example of middleware for Django 1.11:
from django.utils.functional import SimpleLazyObject
from organization.models import OrganizationMember
from django.core.exceptions import ObjectDoesNotExist
def get_active_member(request):
try:
active_member = OrganizationMember.objects.get(user=request.user)
except (ObjectDoesNotExist, TypeError):
active_member = None
return active_member
class OrganizationMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
request.active_member = SimpleLazyObject(lambda: get_active_member(request))
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
Daniel Dubovski's solution is probably the best in most cases.
The problem with the lazy object approach is if you need to rely on the side effects. In my case, I need something to happen for each request, no matter what.
If I'd use a special value like request.custom_prop, it has to be evaluated for each request for the side effects to happen. I noticed that other people are setting request.user, but it doesn't work for me since some middleware or authentication class overwrites this property.
What if DRF supported its own middleware? Where could I plug it in? The easiest way in my case (I don't need to access the request object, only the authenticated user) seems to be to hook into the authentication class itself:
from rest_framework.authentication import TokenAuthentication
class TokenAuthenticationWithSideffects(TokenAuthentication):
def authenticate(self, request):
user_auth_tuple = super().authenticate(request)
if user_auth_tuple is None:
return
(user, token) = user_auth_tuple
# Do stuff with the user here!
return (user, token)
Then I could just replace this line in my settings:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
#"rest_framework.authentication.TokenAuthentication",
"my_project.authentication.TokenAuthenticationWithSideffects",
),
# ...
}
I'm not promoting this solution, but maybe it will help someone else.
Pros:
It to solves this specific problem
There's no double authentication
Easy to maintain
Cons:
Not tested in production
Things happen in an unexpected place
Side effects...
I know it's not exactly answering the 'can we access that from the middleware' question, but I think it's a more elegant solution VS doing the same work in the middleware VS what DRJ does in its base view class. At least for what I needed, it made more sense to add here.
Basically, I'm just overriding the method 'perform_authentication()' from DRF's code, since I needed to add more things related to the current user in the request. The method just originally call 'request.user'.
class MyGenericViewset(viewsets.GenericViewSet):
def perform_authentication(self, request):
request.user
if request.user and request.user.is_authenticated():
request.my_param1 = 'whatever'
After that in your own views, instead of settings APIView from DRF as a parent class, simply set that class as a parent.
I wasn't quite happy with the solutions out there. Here's a solution that uses some DRF internals to make sure that the correct authentication is applied in the middleware, even if the view has specific permissions classes. It uses the middleware hook process_view which gives us access to the view we're about to hit:
class CustomTenantMiddleware():
def process_view(self, request, view_func, view_args, view_kwargs):
# DRF saves the class of the view function as the .cls property
view_class = view_func.cls
try:
# We need to instantiate the class
view = view_class()
# And give it an action_map. It's not relevant for us, but otherwise it errors.
view.action_map = {}
# Here's our fully formed and authenticated (or not, depending on credentials) request
request = view.initialize_request(request)
except (AttributeError, TypeError):
# Can't initialize the request from this view. Fallback to using default permission classes
request = APIView().initialize_request(request)
# Here the request is fully formed, with the correct permissions depending on the view.
Note that this doesn't avoid having to authenticate twice. DRF will still happily authenticate right afterwards.
I had the same issue and decided to change my design. Instead of using a Middleware I simply monkey-patch rest_framework.views.APIView.
In my case I needed to patch check_permissions but you can patch whatever fits your problem. Have a look at the the source code.
settings.py
INSTALLED_APPS = [
..
'myapp',
]
myapp/patching.py
import sys
from rest_framework.views import APIView as OriginalAPIView
class PatchedAPIView(OriginalAPIView):
def check_permissions(self, request):
print(f"We should do something with user {request.user}"
return OriginalAPIView.check_permissions(self, request)
# We replace the Django REST view with our patched one
sys.modules['rest_framework'].views.APIView = PatchedAPIView
myapp/__init__.py
from .patching import *

Django Caching for Authenticated Users Only

Question
In Django, how can create a single cached version of a page (same for all users) that's only visible to authenticated users?
Setup
The pages I wish to cache are only available to authenticated users (they use #login_required on the view). These pages are the same for all authenticated users (e.g. no need to setup vary_on_headers based on unique users).
However, I don't want these cached pages to be visible to non-authenticated users.
What I've tried so far
Page level cache (shows pages intended for logged in users to non-logged in users)
Looked into using vary_on_headers, but I don't need individually cached pages for each user
I checked out template fragment caching, but unless I'm confused, this won't meet my needs
Substantial searching (seems that everyone wants to do the reverse)
Thanks!
Example View
#login_required
#cache_page(60 * 60)
def index(request):
'''Display the home page'''
return render(request, 'index.html')
settings.py (relevant portion)
# Add the below for memcache
MIDDLEWARE_CLASSES += (
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
)
# Enable memcache
# https://devcenter.heroku.com/articles/memcache#using_memcache_from_python
CACHES = {
'default': {
'BACKEND': 'django_pylibmc.memcached.PyLibMCCache'
}
}
Solution
Based on the answer by #Tisho I solved this problem by
Creating a decorators.py file in my app
Adding the below code to it
Importing the function in views.py
Applying it as a decorator to the views I wanted to cache for logged in users only
decorators.py
from functools import wraps
from django.views.decorators.cache import cache_page
from django.utils.decorators import available_attrs
def cache_on_auth(timeout):
def decorator(view_func):
#wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated():
return cache_page(timeout)(view_func)(request, *args, **kwargs)
else:
return view_func(request, *args, **kwargs)
return _wrapped_view
return decorator
For logged in users, it would cache the page (or serve them the cached page) for non-logged in users, it would just give them the regular view, which was decorated with #login_required and would require them to login.
The default cache_page decorator accepts a variable called key_prefix. However, it can be passed as a string parameter only. So you can write your own decorator, that will dynamically modify this prefix_key based on the is_authenticated value. Here is an example:
from django.views.decorators.cache import cache_page
def cache_on_auth(timeout):
def decorator(view_func):
#wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs):
return cache_page(timeout, key_prefix="_auth_%s_" % request.user.is_authenticated())(view_func)(request, *args, **kwargs)
return _wrapped_view
return decorator
and then use it on the view:
#cache_on_auth(60*60)
def myview(request)
Then, the generated cache_key will look like:
cache key:
views.decorators.cache.cache_page._auth_False_.GET.123456.123456
if the user is authenticated, and
cache key:
views.decorators.cache.cache_page._auth_True_.GET.789012.789012
if the user is not authenticated.
If the #wrap decorator in the #Tisho answer makes your brain hurt, or if an explicit solution is better than an implicit one, here's a simple procedural way to serve different cache results:
from django.views.decorators.cache import cache_page
def index(request):
"""
:type request: HttpRequest
"""
is_authenticated = request.user.is_authenticated()
if is_authenticated:
return render_user(request)
else:
return render_visitor(request)
#cache_page(5, key_prefix='user_cache')
def render_user(request):
print 'refreshing user_cache'
return render(request, 'home-user.html', {})
#cache_page(10, key_prefix='visitor_cache')
def render_visitor(request):
print 'refreshing visitor_cache'
return render(request, 'home-visitor.html', {})
I'd advice against using the cache middleware if you want fine tuning of your caching abilities.
However, if you do want to persist keeping it, you could try something like (not saying it would work as is, but something similar to it):
#never_cache
def dynamic_index(request):
# do dynamic stuff
def cached_index(request):
return dynamic_index(request)
#never_cache
def index(request):
if request.user.is_authenticaded():
return cached_index(request)
return dynamic_index(request)
Worst case scenario, you can use cache.set('view_name', template_rendering_result), and cache.get, to just cache the HTML manually.
Here's an updated version of #Tisho's answer that works for Django 3.0 (the old version no longer works since Python 2 support was dropped in Django 3.0):
from django.views.decorators.cache import cache_page
from functools import wraps, WRAPPER_ASSIGNMENTS
def cache_on_auth(timeout):
"""
Caches views up to two times: Once for authenticated users, and
once for unauthenticated users.
"""
def decorator(view_func):
#wraps(view_func, assigned=WRAPPER_ASSIGNMENTS)
def _wrapped_view(request, *args, **kwargs):
result = cache_page(
timeout,
key_prefix=(f"_auth_{request.user.is_authenticated}_"))
return result(view_func)(request, *args, **kwargs)
return _wrapped_view
return decorator
I know this is a very old question, but we have a new alternative to solve this.
You can use the decorator cache_control passing private as True to protect your data.

Categories