fix: stop syncing email in panel descriptions
This commit is contained in:
@@ -1014,7 +1014,6 @@ def _telegram_id_for_user(user: User) -> Optional[int]:
|
|||||||
|
|
||||||
def _panel_description_for_user(user: User) -> str:
|
def _panel_description_for_user(user: User) -> str:
|
||||||
lines = [
|
lines = [
|
||||||
user.email or "",
|
|
||||||
user.username or "",
|
user.username or "",
|
||||||
user.first_name or "",
|
user.first_name or "",
|
||||||
user.last_name or "",
|
user.last_name or "",
|
||||||
|
|||||||
@@ -143,6 +143,15 @@ def _panel_identity_update_payload(user: User, description_text: str) -> dict[st
|
|||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
def _panel_description_for_user(user: User) -> str:
|
||||||
|
lines = [
|
||||||
|
user.username or "",
|
||||||
|
user.first_name or "",
|
||||||
|
user.last_name or "",
|
||||||
|
]
|
||||||
|
return "\n".join(line for line in lines if line).strip()
|
||||||
|
|
||||||
|
|
||||||
def _datetime_matches(current: Optional[datetime], desired: datetime) -> bool:
|
def _datetime_matches(current: Optional[datetime], desired: datetime) -> bool:
|
||||||
if current is None:
|
if current is None:
|
||||||
return False
|
return False
|
||||||
@@ -454,17 +463,7 @@ def _panel_identity_payload_with_expiry(
|
|||||||
*,
|
*,
|
||||||
expire_at: datetime,
|
expire_at: datetime,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
description_text = "\n".join(
|
payload = _panel_identity_update_payload(user, _panel_description_for_user(user))
|
||||||
line
|
|
||||||
for line in [
|
|
||||||
user.email or "",
|
|
||||||
user.username or "",
|
|
||||||
user.first_name or "",
|
|
||||||
user.last_name or "",
|
|
||||||
]
|
|
||||||
if line
|
|
||||||
)
|
|
||||||
payload = _panel_identity_update_payload(user, description_text)
|
|
||||||
payload["expireAt"] = expire_at.isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
payload["expireAt"] = expire_at.isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
||||||
if expire_at > datetime.now(timezone.utc):
|
if expire_at > datetime.now(timezone.utc):
|
||||||
payload["status"] = "ACTIVE"
|
payload["status"] = "ACTIVE"
|
||||||
@@ -1013,16 +1012,7 @@ async def _perform_sync_impl(
|
|||||||
# Ensure panel description contains Telegram fields
|
# Ensure panel description contains Telegram fields
|
||||||
try:
|
try:
|
||||||
if panel_uuid and existing_user and not is_duplicate_panel_identity:
|
if panel_uuid and existing_user and not is_duplicate_panel_identity:
|
||||||
description_text = "\n".join(
|
description_text = _panel_description_for_user(existing_user)
|
||||||
line
|
|
||||||
for line in [
|
|
||||||
existing_user.email or "",
|
|
||||||
existing_user.username or "",
|
|
||||||
existing_user.first_name or "",
|
|
||||||
existing_user.last_name or "",
|
|
||||||
]
|
|
||||||
if line
|
|
||||||
)
|
|
||||||
# Update description only when it differs from the current one on panel
|
# Update description only when it differs from the current one on panel
|
||||||
desired_description = description_text.strip()
|
desired_description = description_text.strip()
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ class ProfileSyncMiddleware(BaseMiddleware):
|
|||||||
if panel_service and db_user.panel_user_uuid:
|
if panel_service and db_user.panel_user_uuid:
|
||||||
description_text = "\n".join(
|
description_text = "\n".join(
|
||||||
[
|
[
|
||||||
db_user.email or "",
|
|
||||||
username_for_display(tg_user.username, with_at=False)
|
username_for_display(tg_user.username, with_at=False)
|
||||||
if sanitized_username is not None
|
if sanitized_username is not None
|
||||||
else "",
|
else "",
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ class PanelIdentityMixin:
|
|||||||
|
|
||||||
def _panel_description_for_user(self, db_user: User) -> str:
|
def _panel_description_for_user(self, db_user: User) -> str:
|
||||||
lines = [
|
lines = [
|
||||||
db_user.email or "",
|
|
||||||
db_user.username or "",
|
db_user.username or "",
|
||||||
db_user.first_name or "",
|
db_user.first_name or "",
|
||||||
db_user.last_name or "",
|
db_user.last_name or "",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from bot.app.web import subscription_webapp # noqa: F401
|
|||||||
from bot.app.web.webapp import account as account_routes
|
from bot.app.web.webapp import account as account_routes
|
||||||
from bot.app.web.webapp.auth import (
|
from bot.app.web.webapp.auth import (
|
||||||
_link_telegram_to_user,
|
_link_telegram_to_user,
|
||||||
|
_panel_description_for_user,
|
||||||
_sync_merged_panel_identity_for_user,
|
_sync_merged_panel_identity_for_user,
|
||||||
_sync_panel_identity_for_user,
|
_sync_panel_identity_for_user,
|
||||||
)
|
)
|
||||||
@@ -72,6 +73,19 @@ class AccountLinkingPanelTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
|
|
||||||
self.assertTrue(result)
|
self.assertTrue(result)
|
||||||
panel_service.update_user_details_on_panel.assert_awaited_once()
|
panel_service.update_user_details_on_panel.assert_awaited_once()
|
||||||
|
_, payload = panel_service.update_user_details_on_panel.await_args.args[:2]
|
||||||
|
self.assertEqual(payload["description"], "alice")
|
||||||
|
self.assertEqual(payload["email"], "linked@example.com")
|
||||||
|
|
||||||
|
def test_panel_description_for_user_excludes_email(self):
|
||||||
|
user = SimpleNamespace(
|
||||||
|
email="linked@example.com",
|
||||||
|
username="alice",
|
||||||
|
first_name="Alice",
|
||||||
|
last_name=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(_panel_description_for_user(user), "alice\nAlice")
|
||||||
|
|
||||||
async def test_merged_panel_identity_deletes_source_before_updating_target(self):
|
async def test_merged_panel_identity_deletes_source_before_updating_target(self):
|
||||||
calls = []
|
calls = []
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ from bot.handlers.admin.sync_admin import (
|
|||||||
_absorb_duplicate_panel_identity,
|
_absorb_duplicate_panel_identity,
|
||||||
_coerce_panel_telegram_id,
|
_coerce_panel_telegram_id,
|
||||||
_description_matches,
|
_description_matches,
|
||||||
|
_panel_description_for_user,
|
||||||
_panel_identity_matches_user,
|
_panel_identity_matches_user,
|
||||||
_panel_identity_needs_full_fetch,
|
_panel_identity_needs_full_fetch,
|
||||||
|
_panel_identity_payload_with_expiry,
|
||||||
_panel_identity_view_for_comparison,
|
_panel_identity_view_for_comparison,
|
||||||
_should_update_lifetime_used_traffic,
|
_should_update_lifetime_used_traffic,
|
||||||
_subscription_update_delta,
|
_subscription_update_delta,
|
||||||
@@ -39,6 +41,55 @@ def test_panel_telegram_id_is_coerced_to_int():
|
|||||||
assert _coerce_panel_telegram_id("") is None
|
assert _coerce_panel_telegram_id("") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_panel_description_for_user_excludes_email():
|
||||||
|
user = SimpleNamespace(
|
||||||
|
email="linked@example.com",
|
||||||
|
username="alice",
|
||||||
|
first_name="Alice",
|
||||||
|
last_name="Smith",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert _panel_description_for_user(user) == "alice\nAlice\nSmith"
|
||||||
|
|
||||||
|
|
||||||
|
def test_panel_identity_match_accepts_list_description_without_email():
|
||||||
|
user = SimpleNamespace(
|
||||||
|
email="linked@example.com",
|
||||||
|
telegram_id=42,
|
||||||
|
username="alice",
|
||||||
|
first_name="Alice",
|
||||||
|
last_name=None,
|
||||||
|
)
|
||||||
|
panel_user = {
|
||||||
|
"description": "alice\nAlice",
|
||||||
|
"email": "linked@example.com",
|
||||||
|
"telegramId": 42,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert _panel_identity_matches_user(
|
||||||
|
panel_user,
|
||||||
|
user,
|
||||||
|
_panel_description_for_user(user),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_panel_identity_payload_with_expiry_keeps_email_out_of_description():
|
||||||
|
expire_at = datetime(2026, 6, 1, 12, 0, tzinfo=timezone.utc)
|
||||||
|
user = SimpleNamespace(
|
||||||
|
email="linked@example.com",
|
||||||
|
telegram_id=42,
|
||||||
|
username="alice",
|
||||||
|
first_name="Alice",
|
||||||
|
last_name=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
payload = _panel_identity_payload_with_expiry(user, expire_at=expire_at)
|
||||||
|
|
||||||
|
assert payload["description"] == "alice\nAlice"
|
||||||
|
assert payload["email"] == "linked@example.com"
|
||||||
|
assert payload["telegramId"] == 42
|
||||||
|
|
||||||
|
|
||||||
def test_panel_identity_match_treats_missing_list_email_as_unknown():
|
def test_panel_identity_match_treats_missing_list_email_as_unknown():
|
||||||
user = SimpleNamespace(
|
user = SimpleNamespace(
|
||||||
email="linked@example.com",
|
email="linked@example.com",
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from bot.services.subscription_service_impl.panel_identity import PanelIdentityMixin
|
||||||
|
|
||||||
|
|
||||||
|
def test_subscription_panel_description_excludes_email():
|
||||||
|
user = SimpleNamespace(
|
||||||
|
email="linked@example.com",
|
||||||
|
username="alice",
|
||||||
|
first_name="Alice",
|
||||||
|
last_name="Smith",
|
||||||
|
telegram_id=42,
|
||||||
|
user_id=42,
|
||||||
|
)
|
||||||
|
|
||||||
|
payload = PanelIdentityMixin()._panel_identity_payload_for_user(user)
|
||||||
|
|
||||||
|
assert payload["description"] == "alice\nAlice\nSmith"
|
||||||
|
assert payload["email"] == "linked@example.com"
|
||||||
|
assert payload["telegramId"] == 42
|
||||||
@@ -69,6 +69,64 @@ class ProfileSyncMiddlewareCacheTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
get_user.assert_awaited_once()
|
get_user.assert_awaited_once()
|
||||||
self.assertEqual(handler.await_count, 2)
|
self.assertEqual(handler.await_count, 2)
|
||||||
|
|
||||||
|
async def test_profile_sync_keeps_email_out_of_panel_description(self):
|
||||||
|
middleware = ProfileSyncMiddleware()
|
||||||
|
handler = AsyncMock(return_value="ok")
|
||||||
|
event = SimpleNamespace()
|
||||||
|
tg_user = SimpleNamespace(
|
||||||
|
id=42,
|
||||||
|
username="alice",
|
||||||
|
first_name="Alice",
|
||||||
|
last_name="Smith",
|
||||||
|
)
|
||||||
|
db_user = SimpleNamespace(
|
||||||
|
user_id=42,
|
||||||
|
telegram_id=42,
|
||||||
|
username="oldalice",
|
||||||
|
first_name="Old",
|
||||||
|
last_name="Smith",
|
||||||
|
email="linked@example.com",
|
||||||
|
panel_user_uuid="panel-42",
|
||||||
|
)
|
||||||
|
panel_service = SimpleNamespace(
|
||||||
|
update_user_details_on_panel=AsyncMock(return_value={"uuid": "panel-42"})
|
||||||
|
)
|
||||||
|
cache_store = {}
|
||||||
|
|
||||||
|
async def fake_get(_settings, key):
|
||||||
|
return cache_store.get(key)
|
||||||
|
|
||||||
|
async def fake_set(_settings, key, value, ttl):
|
||||||
|
cache_store[key] = value
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"session": AsyncMock(),
|
||||||
|
"event_from_user": tg_user,
|
||||||
|
"settings": self._settings(),
|
||||||
|
"panel_service": panel_service,
|
||||||
|
}
|
||||||
|
with (
|
||||||
|
patch.object(profile_sync_module, "cache_get_json", fake_get),
|
||||||
|
patch.object(profile_sync_module, "cache_set_json", fake_set),
|
||||||
|
patch.object(
|
||||||
|
profile_sync_module.user_dal,
|
||||||
|
"get_user_by_telegram_id",
|
||||||
|
AsyncMock(return_value=db_user),
|
||||||
|
),
|
||||||
|
patch.object(
|
||||||
|
profile_sync_module.user_dal,
|
||||||
|
"update_user",
|
||||||
|
AsyncMock(return_value=db_user),
|
||||||
|
),
|
||||||
|
):
|
||||||
|
result = await middleware(handler, event, data)
|
||||||
|
|
||||||
|
self.assertEqual(result, "ok")
|
||||||
|
panel_service.update_user_details_on_panel.assert_awaited_once()
|
||||||
|
_, payload = panel_service.update_user_details_on_panel.await_args.args[:2]
|
||||||
|
self.assertEqual(payload["description"], "alice\nAlice\nSmith")
|
||||||
|
self.assertEqual(payload["email"], "linked@example.com")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user