django-cookie-consent
django-cookie-consent copied to clipboard
CleanCookiesMiddleware does not work
It seems that at the current state the CleanCookiesMiddleware does not work in Django 2.x as is:
TypeError: 'CleanCookiesMiddleware' object is not callable
This error is solved by:
def __call__(self, request):
response = self.get_response(request)
self.process_response(request, response)
return response
However, then a new error appears: cookie_consent\middleware.py", line 43, in process_response if group_version < cookie.get_version() and not settings.COOKIE_CONSENT_OPT_OUT: TypeError: '<' not supported between instances of 'NoneType' and 'str'
I think this one can be fixed by modifying the code as below, but I might have missed something as I am not understanding the code to the last bit
def process_response(self, request, response):
if not is_cookie_consent_enabled(request):
return response
cookie_dic = get_cookie_dict_from_request(request)
for cookie_group in all_cookie_groups().values():
if not cookie_group.is_deletable:
continue
group_version = cookie_dic.get(cookie_group.varname, None)
for cookie in cookie_group.cookie_set.all():
if cookie.name not in request.COOKIES:
continue
if group_version == None:
if not settings.COOKIE_CONSENT_OPT_OUT:
response.delete_cookie(
smart_str(cookie.name),
cookie.path, cookie.domain
)
continue
if group_version == settings.COOKIE_CONSENT_DECLINE:
response.delete_cookie(smart_str(cookie.name),
cookie.path, cookie.domain)
if group_version < cookie.get_version() and not settings.COOKIE_CONSENT_OPT_OUT:
response.delete_cookie(
smart_str(cookie.name),
cookie.path, cookie.domain
)
return response
Is there a work in progress regarding this issue ?
@ThomSawyer No there is not. If you could fix it that would be great. It looks like your recent PR attempts to fix it. Just making sure does your PR officially fix this?
I was able to reproduce the first part of this issue.
Add to your settings.py MIDDLEWARE setting:
'cookie_consent.middleware.CleanCookiesMiddleware',
A guide to setting the middleware should really be added to the docs.
def __call__(self, request):
response = self.get_response(request)
self.process_response(request, response)
return response
^That code is definitely needed. self.process_response(request, response) is needed to call the process_response function in the middleware.
if cookie.name not in str(request.COOKIES)
^We need to make request.COOKIES a string because it is a dictionary. The loop will continue to the next iteration if we do not change this. The if statement will always be true if we compare cookie.name string to request.COOKIES dictionary.
group_version = cookie_dic.get(cookie_group.varname, None)
^This is returning None in the case of vocationeers code. It is not finding the timestamp version for a group because neither cookies are accepted or declined. See models.py to see the version functions. We can use vocationeers code for if group_version == None:
if we would like or use if group_version == None and not settings.COOKIE_CONSENT_OPT_OUT:
To actually test if the middleware works and the cookies are deleted automatically when declined or not accepted/declined, in your database cookies table you must provide all cookies for that group with the exact cookie name, path, and domain.
For example, for a stripe cookie group I have a cookie called __stripe_sid. I accepted the stripe cookies so I could get that __stripe_sid cookie to show up in my browser. Then as soon as I hit decline, the __stripe_sid was automatically deleted thanks to the middleware.
The only problem though is if these third party websites add more cookies without you knowing and they do not match what you have in your database, there will be some extra cookies still remaining in the browser I imagine.
These are some comments I made in the current code to help people understand what is going on (use print statements whenever possible):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
self.process_response(request, response)
return response
def process_response(self, request, response):
#if COOKIE_CONSENT_ENABLED setting is false then return a response
if not is_cookie_consent_enabled(request):
return response
#returns a dictionary of cookie groups and their -1 (declined) or accepted timestamp values
cookie_dic = get_cookie_dict_from_request(request)
#from the cache, loop through all cookie groups
for cookie_group in all_cookie_groups().values():
#if a specific cookie group database value is not deleteable (false), go to the next loop iteration
if not cookie_group.is_deletable:
continue
#returns timestamps or -1s from the browser cookies
group_version = cookie_dic.get(cookie_group.varname, None)
#loop through all database cookie groups cookies
for cookie in cookie_group.cookie_set.all():
#if the cookie name from the database is not in the browser cookie_consent cookie group, go to the next loop iteration
if cookie.name not in str(request.COOKIES): #Need to change the request.COOKIES dictionary to a string for the if statement to work.
continue
#if the cookie group version of -1 equals the COOKIE_CONSENT_DECLINE value of -1
if group_version == settings.COOKIE_CONSENT_DECLINE:
#delete the declined cookie with django built in response.delete_cookie
response.delete_cookie(smart_str(cookie.name),
cookie.path, cookie.domain)
#if the cookie group version timestamp or -1 is less than the cookie version timestamp or -1 and the settings.COOKIE_CONSENT_OPT_OUT setting is false
if group_version < cookie.get_version() and not settings.COOKIE_CONSENT_OPT_OUT:
#delete the declined cookie with django built in response.delete_cookie
response.delete_cookie(
smart_str(cookie.name),
cookie.path, cookie.domain
)
return response
The quickest way to fix this without waiting for the PR is to create your own Middleware. Create a middleware.py file in your project, like this:
from cookie_consent.middleware import CleanCookiesMiddleware
from django.utils.deprecation import MiddlewareMixin
class CleanCookiesFixMiddleware(CleanCookiesMiddleware, MiddlewareMixin):
pass
Then use this Middleware in your settings.py, instead of the one from the package:
MIDDLEWARE = [
...,
'yourpackagename.middleware.CleanCookiesFixMiddleware',
]
That's it! The problem is that the CleanCookiesMiddleware
included in the package is a plain object lacking some needed methods. Compositing that object (to get the process_response
method) with the MiddlewareMixin
from Django itself (to get the needed methods) does the trick with very little code.
Fixed via #80