Skip to content

Instantly share code, notes, and snippets.

@Schnouki
Last active February 10, 2021 02:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Schnouki/32603c54bcd126fa86ea1fa15ac1b82a to your computer and use it in GitHub Desktop.
Save Schnouki/32603c54bcd126fa86ea1fa15ac1b82a to your computer and use it in GitHub Desktop.
WhiteNoiseMiddleware that restrics access to sourcemaps to authorized users
import fnmatch
from django.conf import settings
from django.http import HttpResponseForbidden
from whitenoise.middleware import WhiteNoiseMiddleware
class AuthenticatedWhiteNoiseMiddleware(WhiteNoiseMiddleware):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.auth_paths = getattr(settings, 'WHITENOISE_AUTHENTICATED_PATHS', None) or []
self.auth_cookie = getattr(settings, 'WHITENOISE_AUTH_COOKIE', None)
self.auth_cookie_domain = getattr(settings, 'WHITENOISE_AUTH_COOKIE_DOMAIN', None)
self.auth_cookie_secure = getattr(settings, 'WHITENOISE_AUTH_COOKIE_SECURE', None)
def __call__(self, request):
response = super().__call__(request)
response = self.process_response(request, response)
return response
def process_response(self, request, response):
if self.auth_cookie and hasattr(request, "user") and request.user.is_staff:
# User is authorized: add the auth cookie.
response.set_signed_cookie(self.auth_cookie, "1",
domain=self.auth_cookie_domain,
secure=self.auth_cookie_secure,
httponly=True)
return response
def serve(self, static_file, request):
if self.auth_cookie:
# Configured to enable authentication, let's do it!
path = request.path_info
auth_needed = any(fnmatch.fnmatch(path, pattern) for pattern in self.auth_paths)
if auth_needed and not request.get_signed_cookie(self.auth_cookie, default=False):
# Not authenticated even if needed: too bad…
return HttpResponseForbidden()
return super().serve(static_file, request)
WHITENOISE_AUTHENTICATED_PATHS = ["*.map"]
WHITENOISE_AUTH_COOKIE = "__static_auth"
WHITENOISE_AUTH_COOKIE_DOMAIN = ".example.com" # Required if statics for www.example.com are on static.example.com
WHITENOISE_AUTH_COOKIE_SECURE = not DEBUG
@omarsumadi
Copy link

Thank you so much for helping me through this from years ago to now. All this makes sense and this code should work in my project - I realize now the mistakes are something I need to correct from my own end.

Unfortunately, I implemented exactly what you are describing, but still I yet to be able to check if the request is authenticated in process_response without it raising an error or exception. Maybe this is because I am using ASGI in django?

I'm going to keep cracking at this, but I think you have given me all the information I need.

Thanks a million!

@omarsumadi
Copy link

omarsumadi commented Dec 2, 2020

django          |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
django          |     response = get_response(request)
django          |   File "/app/edsproject/users/middleware.py", line 72, in __call__
django          |     response = self.process_response(request, response)
django          |   File "/app/edsproject/users/middleware.py", line 77, in process_response
django          |     print(request.user.is_authenticated)
django          | AttributeError: 'ASGIRequest' object has no attribute 'user'

Oh - I think perhaps I misinterpreted this whole project. Here's what I learned:

Initial Expectation:

  • On every static file request, we check if the user is authenticated. Because we are using cookies, we are somehow increasing performance, but I really didn't understand what is actually going on.

Actuality:

  • We go to the Login URL, we log in. (In the very beginning)
  • Response is eventually routed to process_response, and an authentication cookie is set as per your code. (this is now the main point of confusion, I need to make sure that my authentication is going to trigger the cookie being set - so is it any response by me from views is going to trigger the cookie setting? For instance, I authenticate my user in my views, is it going to route to process_response after the view is completed to add the cookie?).
  • Any subsequent times we are logged in and access a URL that leads to a view, the authentication cookies is added/refreshed via routing eventually to process_response when any response is being sent back from views.

On Static files

  • We placed Whitenoise middleware / now our custom middleware before authentication middleware's, so any requests that go through (as you said requests are propagated through middleware's linearly, not backwards like process_response) will NOT be present (getattr request user).
  • Because we are intending for Whitenoise to NOT hit authentication, and the fact that whitenoise will be first triggered before authentication middleware's, all requests for the user attribute is going to throw an exception/not be there.
  • However, because we set a cookie for authentication, we can just check that cookie instead without needing the authentication framework to be in place.
  • Then we check file paths with that cookie's status as signed or not acting as our weapon of authentication.

This explains why requests to URLs -> URLs had process_response working as normal, but for staticfiles, process_response was triggering the above error. But the above error is supposed to be there because we aren't checking authentication via Django but via cookies to bypass the authentication in total. Again, we placed the middleware before authentication thus any requests will NOT have authentication checking capabilities.

Sorry - I'm so new to all this i'm just having a hard time with the entire process of execution to get this working. Again, sorry for bothering you a lot about this - I'm happy to donate as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment