Session falls back to a dummy object now if secret_key is missing.

This makes it possible to still read-only access the empty session but
requires the secret key to be set for write access.  The error message
raised explains that.  This closes #10.
This commit is contained in:
Armin Ronacher 2010-04-17 14:10:47 +02:00
parent 791cdb28f5
commit 5310fc3822
2 changed files with 34 additions and 3 deletions

View file

@ -68,6 +68,22 @@ class _RequestGlobals(object):
pass pass
class _NullSession(SecureCookie):
"""Class used to generate nicer error messages if sessions are not
available. Will still allow read-only access to the empty session
but fail on setting.
"""
def _fail(self, *args, **kwargs):
raise RuntimeError('the session is unavailable because no secret '
'key was set. Set the secret_key on the '
'application to something unique and secret')
__setitem__ = __delitem__ = clear = pop = popitem = \
update = setdefault = _fail
del _fail
class _RequestContext(object): class _RequestContext(object):
"""The request context contains all request relevant information. It is """The request context contains all request relevant information. It is
created at the beginning of the request and pushed to the created at the beginning of the request and pushed to the
@ -80,6 +96,8 @@ class _RequestContext(object):
self.url_adapter = app.url_map.bind_to_environ(environ) self.url_adapter = app.url_map.bind_to_environ(environ)
self.request = app.request_class(environ) self.request = app.request_class(environ)
self.session = app.open_session(self.request) self.session = app.open_session(self.request)
if self.session is None:
self.session = _NullSession()
self.g = _RequestGlobals() self.g = _RequestGlobals()
self.flashes = None self.flashes = None
@ -384,8 +402,7 @@ class Flask(object):
object) object)
:param response: an instance of :attr:`response_class` :param response: an instance of :attr:`response_class`
""" """
if session is not None: session.save_cookie(response, self.session_cookie_name)
session.save_cookie(response, self.session_cookie_name)
def add_url_rule(self, rule, endpoint, **options): def add_url_rule(self, rule, endpoint, **options):
"""Connects a URL rule. Works exactly like the :meth:`route` """Connects a URL rule. Works exactly like the :meth:`route`
@ -603,7 +620,7 @@ class Flask(object):
instance of :attr:`response_class`. instance of :attr:`response_class`.
""" """
session = _request_ctx_stack.top.session session = _request_ctx_stack.top.session
if session is not None: if not isinstance(session, _NullSession):
self.save_session(session, response) self.save_session(session, response)
for handler in self.after_request_funcs: for handler in self.after_request_funcs:
response = handler(response) response = handler(response)

View file

@ -72,6 +72,20 @@ class BasicFunctionality(unittest.TestCase):
assert c.post('/set', data={'value': '42'}).data == 'value set' assert c.post('/set', data={'value': '42'}).data == 'value set'
assert c.get('/get').data == '42' assert c.get('/get').data == '42'
def test_missing_session(self):
app = flask.Flask(__name__)
def expect_exception(f, *args, **kwargs):
try:
f(*args, **kwargs)
except RuntimeError, e:
assert e.args and 'session is unavailable' in e.args[0]
else:
assert False, 'expected exception'
with app.test_request_context():
assert flask.session.get('missing_key') is None
expect_exception(flask.session.__setitem__, 'foo', 42)
expect_exception(flask.session.pop, 'foo')
def test_request_processing(self): def test_request_processing(self):
app = flask.Flask(__name__) app = flask.Flask(__name__)
evts = [] evts = []