Browse Source

Added finer control over the session cookie parameters

pull/311/head
Armin Ronacher 14 years ago
parent
commit
ccf464189b
  1. 1
      CHANGES
  2. 18
      docs/config.rst
  3. 6
      flask/app.py
  4. 28
      flask/sessions.py
  5. 22
      flask/testsuite/basic.py

1
CHANGES

@ -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
------------- -------------

18
docs/config.rst

@ -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
---------------------- ----------------------

6
flask/app.py

@ -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

28
flask/sessions.py

@ -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)

22
flask/testsuite/basic.py

@ -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):

Loading…
Cancel
Save