Skip to content

Commit 64bde3e

Browse files
authored
Merge pull request #3207 from takluyver/clearing-cookies
Fix clearing two cookies with the same name
2 parents b439a9f + 0f0fe84 commit 64bde3e

File tree

1 file changed

+34
-4
lines changed

1 file changed

+34
-4
lines changed

notebook/base/handlers.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Copyright (c) Jupyter Development Team.
44
# Distributed under the terms of the Modified BSD License.
55

6+
import datetime
67
import functools
78
import json
89
import mimetypes
@@ -15,15 +16,17 @@
1516
try:
1617
# py3
1718
from http.client import responses
19+
from http.cookies import Morsel
1820
except ImportError:
1921
from httplib import responses
22+
from Cookie import Morsel
2023
try:
2124
from urllib.parse import urlparse # Py 3
2225
except ImportError:
2326
from urlparse import urlparse # Py 2
2427

2528
from jinja2 import TemplateNotFound
26-
from tornado import web, gen, escape
29+
from tornado import web, gen, escape, httputil
2730
from tornado.log import app_log
2831

2932
from notebook._sysinfo import get_sys_info
@@ -91,14 +94,41 @@ def set_default_headers(self):
9194
# for example, so just ignore)
9295
self.log.debug(e)
9396

97+
def force_clear_cookie(self, name, path="/", domain=None):
98+
"""Deletes the cookie with the given name.
99+
100+
Tornado's cookie handling currently (Jan 2018) stores cookies in a dict
101+
keyed by name, so it can only modify one cookie with a given name per
102+
response. The browser can store multiple cookies with the same name
103+
but different domains and/or paths. This method lets us clear multiple
104+
cookies with the same name.
105+
106+
Due to limitations of the cookie protocol, you must pass the same
107+
path and domain to clear a cookie as were used when that cookie
108+
was set (but there is no way to find out on the server side
109+
which values were used for a given cookie).
110+
"""
111+
name = escape.native_str(name)
112+
expires = datetime.datetime.utcnow() - datetime.timedelta(days=365)
113+
114+
morsel = Morsel()
115+
morsel.set(name, '', '""')
116+
morsel['expires'] = httputil.format_timestamp(expires)
117+
morsel['path'] = path
118+
if domain:
119+
morsel['domain'] = domain
120+
self.add_header("Set-Cookie", morsel.OutputString())
121+
94122
def clear_login_cookie(self):
95123
cookie_options = self.settings.get('cookie_options', {})
96124
path = cookie_options.setdefault('path', self.base_url)
97125
self.clear_cookie(self.cookie_name, path=path)
98126
if path and path != '/':
99-
# also clear cookie on / to ensure old cookies
100-
# are cleared after the change in path behavior.
101-
self.clear_cookie(self.cookie_name)
127+
# also clear cookie on / to ensure old cookies are cleared
128+
# after the change in path behavior (changed in notebook 5.2.2).
129+
# N.B. This bypasses the normal cookie handling, which can't update
130+
# two cookies with the same name. See the method above.
131+
self.force_clear_cookie(self.cookie_name)
102132

103133
def get_current_user(self):
104134
if self.login_handler is None:

0 commit comments

Comments
 (0)