Allow web sessions in direct HTTP mode
Debian Trixie package bundle / packages (push) Successful in 21m18s
Debian Trixie package bundle / packages (push) Successful in 21m18s
This commit is contained in:
+11
-5
@@ -59,7 +59,7 @@ from bongo_web.card_store import (CardNotFound, CardPathError,
|
||||
from bongo_web.dav import (card_multistatus, event_multistatus, multistatus,
|
||||
options_headers, parse_card_report, parse_report,
|
||||
requested_properties)
|
||||
from bongo_web.session import (COOKIE_NAME, SessionStore,
|
||||
from bongo_web.session import (SessionStore, cookie_name,
|
||||
expired_session_cookie, session_cookie)
|
||||
from bongo_web.tasks import TaskStore
|
||||
from bongo_web.identities import IdentityStore
|
||||
@@ -375,9 +375,12 @@ class BongoWebHandler(BaseHTTPRequestHandler):
|
||||
cookies = SimpleCookie(self.headers.get("Cookie", ""))
|
||||
except Exception:
|
||||
return None
|
||||
morsel = cookies.get(COOKIE_NAME)
|
||||
morsel = cookies.get(cookie_name(self._secure_cookies()))
|
||||
return morsel.value if morsel else None
|
||||
|
||||
def _secure_cookies(self):
|
||||
return bool(self.server.config.get("external_https", False))
|
||||
|
||||
def _session(self):
|
||||
return self.server.sessions.get(self._session_token())
|
||||
|
||||
@@ -743,7 +746,8 @@ class BongoWebHandler(BaseHTTPRequestHandler):
|
||||
token, csrf_token = self.server.sessions.create(user, store_cookie)
|
||||
self._json(HTTPStatus.CREATED,
|
||||
{"user": user, "csrf_token": csrf_token},
|
||||
{"Set-Cookie": session_cookie(token, lifetime),
|
||||
{"Set-Cookie": session_cookie(
|
||||
token, lifetime, self._secure_cookies()),
|
||||
"Vary": "Cookie"})
|
||||
return
|
||||
user = str(data.get("user", "")).strip()
|
||||
@@ -789,7 +793,8 @@ class BongoWebHandler(BaseHTTPRequestHandler):
|
||||
token, csrf_token = self.server.sessions.create(user, store_cookie)
|
||||
self._json(HTTPStatus.CREATED,
|
||||
{"user": user, "csrf_token": csrf_token},
|
||||
{"Set-Cookie": session_cookie(token, lifetime),
|
||||
{"Set-Cookie": session_cookie(
|
||||
token, lifetime, self._secure_cookies()),
|
||||
"Vary": "Cookie"})
|
||||
|
||||
def _security_session(self):
|
||||
@@ -1113,7 +1118,8 @@ class BongoWebHandler(BaseHTTPRequestHandler):
|
||||
self.server.sessions.delete(token)
|
||||
revoke(session["user"], session["store_cookie"])
|
||||
self._empty(HTTPStatus.NO_CONTENT,
|
||||
{"Set-Cookie": expired_session_cookie(), "Vary": "Cookie"})
|
||||
{"Set-Cookie": expired_session_cookie(
|
||||
self._secure_cookies()), "Vary": "Cookie"})
|
||||
|
||||
def do_PUT(self):
|
||||
path = self._path()
|
||||
|
||||
@@ -33,7 +33,10 @@ import sqlite3
|
||||
import time
|
||||
|
||||
|
||||
COOKIE_NAME = "__Host-bongo-session"
|
||||
SECURE_COOKIE_NAME = "__Host-bongo-session"
|
||||
INSECURE_COOKIE_NAME = "bongo-session"
|
||||
# Retain the historic public name for callers which assume the secure mode.
|
||||
COOKIE_NAME = SECURE_COOKIE_NAME
|
||||
|
||||
|
||||
class SessionStore:
|
||||
@@ -106,11 +109,17 @@ class SessionStore:
|
||||
(self._hash(token),))
|
||||
|
||||
|
||||
def session_cookie(token, max_age):
|
||||
return (f"{COOKIE_NAME}={token}; Path=/; Max-Age={int(max_age)}; "
|
||||
"Secure; HttpOnly; SameSite=Strict")
|
||||
def cookie_name(secure=True):
|
||||
return SECURE_COOKIE_NAME if secure else INSECURE_COOKIE_NAME
|
||||
|
||||
|
||||
def expired_session_cookie():
|
||||
return (f"{COOKIE_NAME}=; Path=/; Max-Age=0; "
|
||||
"Secure; HttpOnly; SameSite=Strict")
|
||||
def session_cookie(token, max_age, secure=True):
|
||||
secure_attribute = " Secure;" if secure else ""
|
||||
return (f"{cookie_name(secure)}={token}; Path=/; Max-Age={int(max_age)};"
|
||||
f"{secure_attribute} HttpOnly; SameSite=Strict")
|
||||
|
||||
|
||||
def expired_session_cookie(secure=True):
|
||||
secure_attribute = " Secure;" if secure else ""
|
||||
return (f"{cookie_name(secure)}=; Path=/; Max-Age=0;"
|
||||
f"{secure_attribute} HttpOnly; SameSite=Strict")
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# /****************************************************************************
|
||||
# * <Novell-copyright>
|
||||
# * Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
# *
|
||||
# * This program is free software; you can redistribute it and/or
|
||||
# * modify it under the terms of version 2 of the GNU General Public License
|
||||
# * as published by the Free Software Foundation.
|
||||
# *
|
||||
# * This program is distributed in the hope that it will be useful,
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# * GNU General Public License for more details.
|
||||
# *
|
||||
# * You should have received a copy of the GNU General Public License
|
||||
# * along with this program; if not, contact Novell, Inc.
|
||||
# *
|
||||
# * To contact Novell about this file by physical or electronic mail, you
|
||||
# * may find current contact information at www.novell.com.
|
||||
# * </Novell-copyright>
|
||||
# ****************************************************************************/
|
||||
|
||||
import unittest
|
||||
|
||||
from bongo_web.session import (expired_session_cookie, session_cookie)
|
||||
|
||||
|
||||
class SessionCookieTests(unittest.TestCase):
|
||||
def test_https_cookie_uses_host_prefix_and_secure_attribute(self):
|
||||
cookie = session_cookie("token", 3600)
|
||||
|
||||
self.assertTrue(cookie.startswith("__Host-bongo-session=token;"))
|
||||
self.assertIn(" Secure;", cookie)
|
||||
self.assertIn(" HttpOnly;", cookie)
|
||||
self.assertIn(" SameSite=Strict", cookie)
|
||||
|
||||
def test_direct_http_cookie_has_separate_name(self):
|
||||
cookie = session_cookie("token", 3600, secure=False)
|
||||
|
||||
self.assertTrue(cookie.startswith("bongo-session=token;"))
|
||||
self.assertNotIn(" Secure;", cookie)
|
||||
self.assertIn(" HttpOnly;", cookie)
|
||||
self.assertIn(" SameSite=Strict", cookie)
|
||||
|
||||
def test_expired_cookie_matches_transport_mode(self):
|
||||
self.assertTrue(expired_session_cookie().startswith(
|
||||
"__Host-bongo-session=;"))
|
||||
insecure = expired_session_cookie(secure=False)
|
||||
self.assertTrue(insecure.startswith("bongo-session=;"))
|
||||
self.assertNotIn(" Secure;", insecure)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user