Cookie based flash errors and notices (a la Rails)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
from hashlib import sha1
import pickle

import settings

COOKIE_NAME_ERROR = 'flash-error'
COOKIE_NAME_NOTICE = 'flash-notice'

def flash_error(msg):
    Flash.set_error = msg

def flash_notice(msg):
    Flash.set_notice = msg

def context_processor(request):
    if COOKIE_NAME_ERROR in request.COOKIES:
        Flash.error = _decode(request.COOKIES[COOKIE_NAME_ERROR])
    elif Flash.set_error:
        Flash.error, Flash.set_error = Flash.set_error, None
        
    if COOKIE_NAME_NOTICE in request.COOKIES:
        Flash.notice = _decode(request.COOKIES[COOKIE_NAME_NOTICE])
    elif Flash.set_notice:
        Flash.notice, Flash.set_notice = Flash.set_notice, None
    
    return {'flash': Flash}

class Middleware(object):
    
    def process_request(self, request):
        Flash.clear()

    def process_response(self, request, response):
        if Flash.set_error:
            response.set_cookie(COOKIE_NAME_ERROR, _encode(Flash.set_error))
        else:
            response.delete_cookie(COOKIE_NAME_ERROR)
        if Flash.set_notice:
            response.set_cookie(COOKIE_NAME_NOTICE, _encode(Flash.set_notice))
        else:
            response.delete_cookie(COOKIE_NAME_NOTICE)
        
        return response

def _encode(value):
    value = pickle.dumps(value)
    hash = sha1(value + settings.SECRET_KEY)
    return '%s$%s' % (hash.hexdigest(), value)

def _decode(cookie):
    s = cookie.split('$', 1)
    if not s or len(s) != 2:
        return None
    hash, value = s
    if sha1(value + settings.SECRET_KEY).hexdigest() != hash:
        return None
    return pickle.loads(value)
        
class Flash(object):
    @classmethod 
    def clear(cls):
        cls.error = None
        cls.notice = None
        cls.set_error = None
        cls.set_notice = None        

More like this

  1. Templatetag startswith + message tuned by io_error 4 years, 11 months ago
  2. Extended rails flash implementation by miracle2k 5 years, 10 months ago
  3. CSS Preprocessor by jokull 5 years, 7 months ago
  4. Photologue wiki-syntax templatetag by yeago 4 years, 7 months ago
  5. "Zoom in" on rendered HTML that the test client returns by peterbe 4 years ago

Comments

sundance (on September 18, 2008):

Hi alexk,

Your code unpickles data from an untrusted source (the cookie). This is a bad idea: http://www.python.org/doc/2.2.1/lib/pickle-sec.html

Besides, you absolutely don't need to store the messages in a cookie, i.e. client-side. Storing them server-side until you get to render them is safer, and arguably makes more sense. Look here for a snippet that does just this: http://www.djangosnippets.org/snippets/1002/

(Unrelatedly, why is Flash a class and not an object?)

Good work otherwise, keep it up! :)

#

carljm (on September 18, 2008):

Sundance is right. Unless this code were integrated with signed cookies, it should be considered dangerously insecure.

#

alexk (on September 21, 2008):

Thanks guys, I will revamp my code.

#

alexk (on September 24, 2008):

I fixed the code. The cookies are now prefixed with a salted hash value which is checked before unpickling. Thanks again!

#

guettli (on December 5, 2008):

I see two problems:

The class Flash is a global variable, which is shared by all threads. I would prefer to store the messages on the request object.

I would use a list of messages. Something like this:

def flash_error(self, msg):
    self.flash(msg, 'error')

def flash(self, msg, loglevel='notice')
    self.flashes.append((msg, loglevel))

#

guettli (on May 20, 2009):

Snippet 1459 is my implementation of cookie based messages.

#

(Forgotten your password?)