Merge branch 'dev' into feature-remote-ocr-2

This commit is contained in:
shamoon
2025-12-07 20:37:56 -08:00
committed by GitHub
70 changed files with 2368 additions and 2055 deletions

View File

@@ -137,3 +137,25 @@ class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
user.save()
handle_social_account_updated(None, request, sociallogin)
return user
def on_authentication_error(
self,
request,
provider,
error=None,
exception=None,
extra_context=None,
):
"""
Just log errors and pass them along.
"""
logger.warning(
f"Social authentication error for provider `{provider!s}`: {error!s} ({exception!s})",
)
return super().on_authentication_error(
request,
provider,
error,
exception,
extra_context,
)

View File

@@ -38,10 +38,19 @@ def handle_social_account_updated(sender, request, sociallogin, **kwargs):
"""
from django.contrib.auth.models import Group
social_account_groups = sociallogin.account.extra_data.get(
extra_data = sociallogin.account.extra_data or {}
social_account_groups = extra_data.get(
"groups",
[],
) # None if not found
) # pre-allauth 65.11.0 structure
if not social_account_groups:
# allauth 65.11.0+ nests claims under `userinfo`/`id_token`
social_account_groups = (
extra_data.get("userinfo", {}).get("groups")
or extra_data.get("id_token", {}).get("groups")
or []
)
if settings.SOCIAL_ACCOUNT_SYNC_GROUPS and social_account_groups is not None:
groups = Group.objects.filter(name__in=social_account_groups)
logger.debug(

View File

@@ -167,3 +167,17 @@ class TestCustomSocialAccountAdapter(TestCase):
self.assertEqual(user.groups.count(), 1)
self.assertTrue(user.groups.filter(name="group1").exists())
self.assertFalse(user.groups.filter(name="group2").exists())
def test_error_logged_on_authentication_error(self):
adapter = get_social_adapter()
request = HttpRequest()
with self.assertLogs("paperless.auth", level="INFO") as log_cm:
adapter.on_authentication_error(
request,
provider="test-provider",
error="Error",
exception="Test authentication error",
)
self.assertTrue(
any("Test authentication error" in message for message in log_cm.output),
)

View File

@@ -192,6 +192,68 @@ class TestSyncSocialLoginGroups(TestCase):
)
self.assertEqual(list(user.groups.all()), [])
@override_settings(SOCIAL_ACCOUNT_SYNC_GROUPS=True)
def test_userinfo_groups(self):
"""
GIVEN:
- Enabled group syncing, and `groups` nested under `userinfo`
WHEN:
- The social login is updated via signal after login
THEN:
- The user's groups are updated using `userinfo.groups`
"""
group = Group.objects.create(name="group1")
user = User.objects.create_user(username="testuser")
sociallogin = Mock(
user=user,
account=Mock(
extra_data={
"userinfo": {
"groups": ["group1"],
},
},
),
)
handle_social_account_updated(
sender=None,
request=HttpRequest(),
sociallogin=sociallogin,
)
self.assertEqual(list(user.groups.all()), [group])
@override_settings(SOCIAL_ACCOUNT_SYNC_GROUPS=True)
def test_id_token_groups_fallback(self):
"""
GIVEN:
- Enabled group syncing, and `groups` only under `id_token`
WHEN:
- The social login is updated via signal after login
THEN:
- The user's groups are updated using `id_token.groups`
"""
group = Group.objects.create(name="group1")
user = User.objects.create_user(username="testuser")
sociallogin = Mock(
user=user,
account=Mock(
extra_data={
"id_token": {
"groups": ["group1"],
},
},
),
)
handle_social_account_updated(
sender=None,
request=HttpRequest(),
sociallogin=sociallogin,
)
self.assertEqual(list(user.groups.all()), [group])
class TestUserGroupDeletionCleanup(TestCase):
"""

View File

@@ -1,6 +1,6 @@
from typing import Final
__version__: Final[tuple[int, int, int]] = (2, 19, 6)
__version__: Final[tuple[int, int, int]] = (2, 20, 1)
# Version string like X.Y.Z
__full_version_str__: Final[str] = ".".join(map(str, __version__))
# Version string like X.Y