Skip to content

Commit e7cb8ec

Browse files
Implement handling of new cookie received from SU platform (#722)
* Refactor fetch method to one location * Improve new cookie handling
1 parent 343b15a commit e7cb8ec

3 files changed

Lines changed: 38 additions & 38 deletions

File tree

cogs/check_su_platform_authorisation.py

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@
44
from enum import Enum
55
from typing import TYPE_CHECKING, override
66

7-
import aiohttp
87
import bs4
98
import discord
109
from discord.ext import tasks
1110

1211
from config import settings
13-
from utils import GLOBAL_SSL_CONTEXT, CommandChecks, TeXBotBaseCog
12+
from utils import CommandChecks, TeXBotBaseCog
1413
from utils.error_capture_decorators import (
1514
capture_guild_does_not_exist_error,
1615
)
16+
from utils.msl import fetch_url_content_with_session
1717

1818
if TYPE_CHECKING:
19-
from collections.abc import Iterable, Mapping, Sequence
19+
from collections.abc import Iterable, Sequence
2020
from collections.abc import Set as AbstractSet
2121
from logging import Logger
2222
from typing import Final
@@ -31,15 +31,6 @@
3131

3232
logger: "Final[Logger]" = logging.getLogger("TeX-Bot")
3333

34-
REQUEST_HEADERS: "Final[Mapping[str, str]]" = {
35-
"Cache-Control": "no-cache",
36-
"Pragma": "no-cache",
37-
"Expires": "0",
38-
}
39-
40-
REQUEST_COOKIES: "Final[Mapping[str, str]]" = {
41-
".AspNet.SharedCookie": settings["SU_PLATFORM_ACCESS_COOKIE"]
42-
}
4334

4435
SU_PLATFORM_PROFILE_URL: "Final[str]" = "https://guildofstudents.com/profile"
4536
SU_PLATFORM_ORGANISATION_URL: "Final[str]" = (
@@ -76,20 +67,10 @@ class SUPlatformAccessCookieStatus(Enum):
7667
class CheckSUPlatformAuthorisationBaseCog(TeXBotBaseCog):
7768
"""Cog class that defines the base functionality for cookie authorisation checks."""
7869

79-
async def _fetch_url_content_with_session(self, url: str) -> str:
80-
"""Fetch the HTTP content at the given URL, using a shared aiohttp session."""
81-
async with (
82-
aiohttp.ClientSession(
83-
headers=REQUEST_HEADERS, cookies=REQUEST_COOKIES
84-
) as http_session,
85-
http_session.get(url=url, ssl=GLOBAL_SSL_CONTEXT) as http_response,
86-
):
87-
return await http_response.text()
88-
8970
async def get_su_platform_access_cookie_status(self) -> SUPlatformAccessCookieStatus:
9071
"""Retrieve the current validity status of the SU platform access cookie."""
9172
response_object: bs4.BeautifulSoup = bs4.BeautifulSoup(
92-
await self._fetch_url_content_with_session(SU_PLATFORM_PROFILE_URL), "html.parser"
73+
await fetch_url_content_with_session(SU_PLATFORM_PROFILE_URL), "html.parser"
9374
)
9475
page_title: bs4.Tag | bs4.NavigableString | None = response_object.find("title")
9576
if not page_title or "Login" in str(page_title):
@@ -99,7 +80,7 @@ async def get_su_platform_access_cookie_status(self) -> SUPlatformAccessCookieSt
9980
organisation_admin_url: str = (
10081
f"{SU_PLATFORM_ORGANISATION_URL}/{settings['ORGANISATION_ID']}"
10182
)
102-
response_html: str = await self._fetch_url_content_with_session(organisation_admin_url)
83+
response_html: str = await fetch_url_content_with_session(organisation_admin_url)
10384

10485
if "admin tools" in response_html.lower():
10586
return SUPlatformAccessCookieStatus.AUTHORISED
@@ -115,7 +96,7 @@ async def get_su_platform_access_cookie_status(self) -> SUPlatformAccessCookieSt
11596
async def get_su_platform_organisations(self) -> "Iterable[str]":
11697
"""Retrieve the MSL organisations the current SU platform cookie has access to."""
11798
response_object: bs4.BeautifulSoup = bs4.BeautifulSoup(
118-
await self._fetch_url_content_with_session(SU_PLATFORM_PROFILE_URL), "html.parser"
99+
await fetch_url_content_with_session(SU_PLATFORM_PROFILE_URL), "html.parser"
119100
)
120101

121102
page_title: bs4.Tag | bs4.NavigableString | None = response_object.find("title")

utils/msl/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .memberships import (
66
fetch_community_group_members_count,
77
fetch_community_group_members_list,
8+
fetch_url_content_with_session,
89
is_id_a_community_group_member,
910
)
1011

@@ -14,5 +15,6 @@
1415
__all__: "Sequence[str]" = (
1516
"fetch_community_group_members_count",
1617
"fetch_community_group_members_list",
18+
"fetch_url_content_with_session",
1719
"is_id_a_community_group_member",
1820
)

utils/msl/memberships.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,28 @@
1414

1515
if TYPE_CHECKING:
1616
from collections.abc import Mapping, Sequence
17+
from http.cookies import Morsel
1718
from logging import Logger
1819
from typing import Final
1920

2021

2122
__all__: "Sequence[str]" = (
2223
"fetch_community_group_members_count",
2324
"fetch_community_group_members_list",
25+
"fetch_url_content_with_session",
2426
"is_id_a_community_group_member",
2527
)
2628

2729

2830
logger: "Final[Logger]" = logging.getLogger("TeX-Bot")
2931

30-
3132
BASE_SU_PLATFORM_WEB_HEADERS: "Final[Mapping[str, str]]" = {
3233
"Cache-Control": "no-cache",
3334
"Pragma": "no-cache",
3435
"Expires": "0",
3536
}
3637

37-
BASE_SU_PLATFORM_WEB_COOKIES: "Final[Mapping[str, str]]" = {
38+
BASE_SU_PLATFORM_WEB_COOKIES: "Mapping[str, str]" = {
3839
".AspNet.SharedCookie": settings["SU_PLATFORM_ACCESS_COOKIE"],
3940
}
4041

@@ -43,21 +44,37 @@
4344
_membership_list_cache: set[int] = set()
4445

4546

46-
async def fetch_community_group_members_list() -> set[int]:
47-
"""
48-
Make a web request to fetch your community group's full membership list.
49-
50-
Returns a set of IDs.
51-
"""
47+
async def fetch_url_content_with_session(url: str) -> str:
48+
"""Fetch the HTTP content at the given URL, using a shared aiohttp session."""
49+
global BASE_SU_PLATFORM_WEB_COOKIES # noqa: PLW0603
5250
async with (
5351
aiohttp.ClientSession(
5452
headers=BASE_SU_PLATFORM_WEB_HEADERS, cookies=BASE_SU_PLATFORM_WEB_COOKIES
5553
) as http_session,
56-
http_session.get(url=MEMBERS_LIST_URL, ssl=GLOBAL_SSL_CONTEXT) as http_response,
54+
http_session.get(url=url, ssl=GLOBAL_SSL_CONTEXT) as http_response,
5755
):
58-
response_html: str = await http_response.text()
56+
returned_asp_cookie: Morsel[str] | None = http_response.cookies.get(
57+
".AspNet.SharedCookie"
58+
)
59+
if returned_asp_cookie is not None and (
60+
returned_asp_cookie.value != BASE_SU_PLATFORM_WEB_COOKIES[".AspNet.SharedCookie"]
61+
):
62+
logger.info("SU platform access cookie was updated by the server; updating local.")
63+
BASE_SU_PLATFORM_WEB_COOKIES = {
64+
".AspNet.SharedCookie": returned_asp_cookie.value,
65+
}
66+
return await http_response.text()
5967

60-
parsed_html: BeautifulSoup = BeautifulSoup(markup=response_html, features="html.parser")
68+
69+
async def fetch_community_group_members_list() -> set[int]:
70+
"""
71+
Make a web request to fetch your community group's full membership list.
72+
73+
Returns a set of IDs.
74+
"""
75+
parsed_html: BeautifulSoup = BeautifulSoup(
76+
markup=await fetch_url_content_with_session(MEMBERS_LIST_URL), features="html.parser"
77+
)
6178

6279
member_ids: set[int] = set()
6380

@@ -72,7 +89,7 @@ async def fetch_community_group_members_list() -> set[int]:
7289

7390
if filtered_table is None:
7491
logger.warning("Membership table with ID %s could not be found.", table_id)
75-
logger.debug(response_html)
92+
logger.debug(parsed_html)
7693
continue
7794

7895
if isinstance(filtered_table, bs4.NavigableString):
@@ -97,7 +114,7 @@ async def fetch_community_group_members_list() -> set[int]:
97114
if not member_ids: # NOTE: this should never be possible, because to fetch the page you need to have admin access, which requires being a member.
98115
NO_MEMBERS_MESSAGE: Final[str] = "No members were found in either membership table."
99116
logger.warning(NO_MEMBERS_MESSAGE)
100-
logger.debug(response_html)
117+
logger.debug(parsed_html)
101118
raise MSLMembershipError(message=NO_MEMBERS_MESSAGE)
102119

103120
_membership_list_cache.clear()

0 commit comments

Comments
 (0)