Skip to content

Commit

Permalink
Merge pull request #144 from corenting/fix/cookie-expiry
Browse files Browse the repository at this point in the history
fix(cookies): set expiry (fix #143)
  • Loading branch information
corenting authored Mar 13, 2024
2 parents b0e02e4 + e68b857 commit 0539d05
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 85 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Version 0.8.2 (not released yet)

- Fix settings cookies expiration as reported by [ValiumBear](https://github.com/ValiumBear) [in](https://github.com/corenting/eddrit/issues/143)

# Version 0.8.1

- Fix galleries without captions
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ init:
.PHONY: format
format:
$(PYTHON) ruff format $(SRC)
$(PYTHON) ruff --fix $(SRC)
$(PYTHON) ruff check --fix $(SRC)
$(BIOME) format --write $(JS_SRC)
$(BIOME) check --apply $(JS_SRC)


.PHONY: style
style:
$(PYTHON) ruff format --check $(SRC)
$(PYTHON) ruff $(SRC)
$(PYTHON) ruff check $(SRC)
$(PYTHON) pyright -- $(SRC)
$(BIOME) lint $(JS_SRC)

Expand Down
15 changes: 14 additions & 1 deletion eddrit/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,24 @@
)
from eddrit.routes.xhr import routes
from eddrit.utils.httpx import raise_if_rate_limited
from eddrit.utils.middlewares import CurrentHostMiddleware, NoReferrerMiddleware
from eddrit.utils.middlewares import (
CookiesRefreshMiddleware,
CurrentHostMiddleware,
NoReferrerMiddleware,
)

middlewares = [
Middleware(NoReferrerMiddleware),
Middleware(CurrentHostMiddleware),
Middleware(
CookiesRefreshMiddleware,
cookies_to_refresh=[
"layout",
"nsfw_popular_all",
"nsfw_thumbnails" "over18",
"thumbnails",
],
),
]

exceptions_handlers = {
Expand Down
18 changes: 3 additions & 15 deletions eddrit/routes/common/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,7 @@

from eddrit import models
from eddrit.models.settings import LayoutMode, ThumbnailsMode


def _get_bool_setting_value_from_cookie(
name: str, cookies: dict[str, str], default: bool = False
) -> bool:
"""Get boolean value of a given setting according to the given cookies.
If setting not present, use default value
"""
if name in cookies:
return cookies[name] == "1"
else:
return default
from eddrit.routes.common.cookies import get_bool_setting_value_from_cookie


def get_templates_common_context(
Expand All @@ -37,10 +25,10 @@ def get_templates_common_context(
thumbnails=ThumbnailsMode(
cookies_source.get("thumbnails", ThumbnailsMode.SUBREDDIT_PREFERENCE.value)
),
nsfw_popular_all=_get_bool_setting_value_from_cookie(
nsfw_popular_all=get_bool_setting_value_from_cookie(
"nsfw_popular_all", cookies_source
),
nsfw_thumbnails=_get_bool_setting_value_from_cookie(
nsfw_thumbnails=get_bool_setting_value_from_cookie(
"nsfw_thumbnails", cookies_source
),
)
Expand Down
35 changes: 35 additions & 0 deletions eddrit/routes/common/cookies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from dataclasses import dataclass
from datetime import UTC, datetime, timedelta

from eddrit.config import DEBUG


@dataclass
class CookieSettings:
secure: bool
http_only: bool
expiration_date: datetime


def get_default_cookie_settings() -> CookieSettings:
"""
Get default cookie settings
"""
return CookieSettings(
secure=not DEBUG,
http_only=True,
expiration_date=datetime.now(tz=UTC) + timedelta(days=90),
)


def get_bool_setting_value_from_cookie(
name: str, cookies: dict[str, str], default: bool = False
) -> bool:
"""Get boolean value of a given setting according to the given cookies.
If setting not present, use default value
"""
if name in cookies:
return cookies[name] == "1"
else:
return default
11 changes: 10 additions & 1 deletion eddrit/routes/pages/over18.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from starlette.routing import Route

from eddrit.routes.common.context import get_templates_common_context
from eddrit.routes.common.cookies import get_default_cookie_settings
from eddrit.templates import templates


Expand All @@ -27,7 +28,15 @@ async def over18_gate_submit(request: Request) -> Response:
body = await request.form()
continue_url = str(body.get("continue", "/"))
res = RedirectResponse(url=continue_url, status_code=302)
res.set_cookie("over18", "1", secure=True, httponly=True)

default_cookie_settings = get_default_cookie_settings()
res.set_cookie(
"over18",
"1",
secure=default_cookie_settings.secure,
httponly=default_cookie_settings.http_only,
expires=default_cookie_settings.expiration_date,
)
return res


Expand Down
8 changes: 5 additions & 3 deletions eddrit/routes/pages/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from starlette.responses import Response
from starlette.routing import Route

from eddrit.config import DEBUG
from eddrit.routes.common.context import get_templates_common_context
from eddrit.routes.common.cookies import get_default_cookie_settings
from eddrit.templates import templates


Expand Down Expand Up @@ -39,12 +39,14 @@ async def settings_submit(request: Request) -> Response:
},
)

default_cookie_settings = get_default_cookie_settings()
for cookie_name, cookie_value in updated_cookies.items():
res.set_cookie(
cookie_name,
cookie_value,
secure=not DEBUG,
httponly=True,
secure=default_cookie_settings.secure,
httponly=default_cookie_settings.http_only,
expires=default_cookie_settings.expiration_date,
)

return res
Expand Down
48 changes: 48 additions & 0 deletions eddrit/utils/middlewares.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,60 @@
import http.cookies
from contextvars import ContextVar
from email.utils import format_datetime

from starlette.datastructures import MutableHeaders
from starlette.requests import Request
from starlette.types import ASGIApp, Message, Receive, Scope, Send

from eddrit.routes.common.cookies import get_default_cookie_settings

_current_host_ctx_var: ContextVar[str] = ContextVar("CURRENT_HOST_CTX_VAR", default="")


class CookiesRefreshMiddleware:
"""Middleware to refresh a specified set of cookies in all responses."""

def __init__(self, app: ASGIApp, cookies_to_refresh: list[str]) -> None:
self.app = app
self.cookies_to_refresh = cookies_to_refresh

async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] != "http":
return await self.app(scope, receive, send)

async def send_with_refreshed_cookies(message: Message) -> None:
if message["type"] == "http.response.start":
initial_request = Request(scope)
headers = MutableHeaders(scope=message)
cookie_default_settings = get_default_cookie_settings()

for cookie_name, cookie_value in initial_request.cookies.items():
if cookie_name not in self.cookies_to_refresh:
continue

# Same code as Response.set_cookie from starlette
updated_cookie = http.cookies.SimpleCookie()
updated_cookie[cookie_name] = cookie_value
updated_cookie[cookie_name]["expires"] = format_datetime(
cookie_default_settings.expiration_date, usegmt=True
)
updated_cookie[cookie_name]["path"] = "/"
updated_cookie[cookie_name]["secure"] = (
cookie_default_settings.secure
)
updated_cookie[cookie_name]["httponly"] = (
cookie_default_settings.http_only
)
updated_cookie[cookie_name]["samesite"] = "lax"
headers.append(
key="set-cookie", value=updated_cookie.output(header="").strip()
)

await send(message)

await self.app(scope, receive, send_with_refreshed_cookies)


class NoReferrerMiddleware:
"""Middleware adding a no-referrer Referrer-Policy on all responses"""

Expand Down
Loading

0 comments on commit 0539d05

Please sign in to comment.