Added finer control over the session cookie parameters

This commit is contained in:
Armin Ronacher 2011-08-30 14:36:50 +02:00
parent 23bf2633f6
commit ccf464189b
5 changed files with 69 additions and 6 deletions

View File

@ -42,6 +42,7 @@ Relase date to be decided, codename to be chosen.
pluggable (class based) views. pluggable (class based) views.
- Fixed an issue where the test client if used with the with statement did not - Fixed an issue where the test client if used with the with statement did not
trigger the execution of the teardown handlers. trigger the execution of the teardown handlers.
- Added finer control over the session cookie parameters.
Version 0.7.3 Version 0.7.3
------------- -------------

View File

@ -70,6 +70,20 @@ The following configuration values are used internally by Flask:
very risky). very risky).
``SECRET_KEY`` the secret key ``SECRET_KEY`` the secret key
``SESSION_COOKIE_NAME`` the name of the session cookie ``SESSION_COOKIE_NAME`` the name of the session cookie
``SESSION_COOKIE_DOMAIN`` the domain for the session cookie. If
this is not set, the cookie will be
valid for all subdomains of
``SERVER_NAME``.
``SESSION_COOKIE_PATH`` the path for the session cookie. If
this is not set the cookie will be valid
for all of ``APPLICATION_ROOT`` or if
that is not set for ``'/'``.
``SESSION_COOKIE_HTTPONLY`` controls if the cookie should be set
with the httponly flag. Defaults to
`True`.
``SESSION_COOKIE_SECURE`` controls if the cookie should be set
with the secure flag. Defaults to
`False`.
``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as ``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as
:class:`datetime.timedelta` object. :class:`datetime.timedelta` object.
``USE_X_SENDFILE`` enable/disable x-sendfile ``USE_X_SENDFILE`` enable/disable x-sendfile
@ -142,7 +156,9 @@ The following configuration values are used internally by Flask:
.. versionadded:: 0.8 .. versionadded:: 0.8
``TRAP_BAD_REQUEST_ERRORS``, ``TRAP_HTTP_EXCEPTIONS``, ``TRAP_BAD_REQUEST_ERRORS``, ``TRAP_HTTP_EXCEPTIONS``,
``APPLICATION_ROOT`` ``APPLICATION_ROOT``, ``SESSION_COOKIE_DOMAIN``,
``SESSION_COOKIE_PATH``, ``SESSION_COOKIE_HTTPONLY``,
``SESSION_COOKIE_SECURE``
Configuring from Files Configuring from Files
---------------------- ----------------------

View File

@ -231,12 +231,16 @@ class Flask(_PackageBoundObject):
'PROPAGATE_EXCEPTIONS': None, 'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None, 'SECRET_KEY': None,
'SESSION_COOKIE_NAME': 'session',
'PERMANENT_SESSION_LIFETIME': timedelta(days=31), 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'USE_X_SENDFILE': False, 'USE_X_SENDFILE': False,
'LOGGER_NAME': None, 'LOGGER_NAME': None,
'SERVER_NAME': None, 'SERVER_NAME': None,
'APPLICATION_ROOT': None, 'APPLICATION_ROOT': None,
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'MAX_CONTENT_LENGTH': None, 'MAX_CONTENT_LENGTH': None,
'TRAP_BAD_REQUEST_ERRORS': False, 'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False 'TRAP_HTTP_EXCEPTIONS': False

View File

@ -123,16 +123,33 @@ class SessionInterface(object):
"""Helpful helper method that returns the cookie domain that should """Helpful helper method that returns the cookie domain that should
be used for the session cookie if session cookies are used. be used for the session cookie if session cookies are used.
""" """
if app.config['SESSION_COOKIE_DOMAIN'] is not None:
return app.config['SESSION_COOKIE_DOMAIN']
if app.config['SERVER_NAME'] is not None: if app.config['SERVER_NAME'] is not None:
# chop of the port which is usually not supported by browsers # chop of the port which is usually not supported by browsers
return '.' + app.config['SERVER_NAME'].rsplit(':', 1)[0] return '.' + app.config['SERVER_NAME'].rsplit(':', 1)[0]
def get_cookie_path(self, app): def get_cookie_path(self, app):
"""Returns the path for which the cookie should be valid. The """Returns the path for which the cookie should be valid. The
default implementation uses the value from the ``APPLICATION_ROOT`` default implementation uses the value from the SESSION_COOKIE_PATH``
configuration variable or uses ``/`` if it's `None`. config var if it's set, and falls back to ``APPLICATION_ROOT`` or
uses ``/`` if it's `None`.
""" """
return app.config['APPLICATION_ROOT'] or '/' return app.config['SESSION_COOKIE_PATH'] or \
app.config['APPLICATION_ROOT'] or '/'
def get_cookie_httponly(self, app):
"""Returns True if the session cookie should be httponly. This
currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
config var.
"""
return app.config['SESSION_COOKIE_HTTPONLY']
def get_cookie_secure(self, app):
"""Returns True if the cookie should be secure. This currently
just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
"""
return app.config['SESSION_COOKIE_SECURE']
def get_expiration_time(self, app, session): def get_expiration_time(self, app, session):
"""A helper method that returns an expiration date for the session """A helper method that returns an expiration date for the session
@ -177,9 +194,12 @@ class SecureCookieSessionInterface(SessionInterface):
expires = self.get_expiration_time(app, session) expires = self.get_expiration_time(app, session)
domain = self.get_cookie_domain(app) domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app) path = self.get_cookie_path(app)
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
if session.modified and not session: if session.modified and not session:
response.delete_cookie(app.session_cookie_name, path=path, response.delete_cookie(app.session_cookie_name, path=path,
domain=domain) domain=domain)
else: else:
session.save_cookie(response, app.session_cookie_name, path=path, session.save_cookie(response, app.session_cookie_name, path=path,
expires=expires, httponly=True, domain=domain) expires=expires, httponly=httponly,
secure=secure, domain=domain)

View File

@ -207,6 +207,28 @@ class BasicFunctionalityTestCase(FlaskTestCase):
rv = app.test_client().get('/', 'http://example.com:8080/') rv = app.test_client().get('/', 'http://example.com:8080/')
self.assert_('path=/bar' in rv.headers['set-cookie'].lower()) self.assert_('path=/bar' in rv.headers['set-cookie'].lower())
def test_session_using_session_settings(self):
app = flask.Flask(__name__)
app.config.update(
SECRET_KEY='foo',
SERVER_NAME='www.example.com:8080',
APPLICATION_ROOT='/test',
SESSION_COOKIE_DOMAIN='.example.com',
SESSION_COOKIE_HTTPONLY=False,
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_PATH='/'
)
@app.route('/')
def index():
flask.session['testing'] = 42
return 'Hello World'
rv = app.test_client().get('/', 'http://www.example.com:8080/test/')
cookie = rv.headers['set-cookie'].lower()
self.assert_('domain=.example.com' in cookie)
self.assert_('path=/;' in cookie)
self.assert_('secure' in cookie)
self.assert_('httponly' not in cookie)
def test_missing_session(self): def test_missing_session(self):
app = flask.Flask(__name__) app = flask.Flask(__name__)
def expect_exception(f, *args, **kwargs): def expect_exception(f, *args, **kwargs):