Coverage for src/backend/InvenTree/InvenTree/sso.py: 15%
65 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
1"""Helper functions for Single Sign On functionality."""
3import json
5from django.contrib.auth.models import Group
6from django.db.models.signals import post_save
7from django.dispatch import receiver
9import structlog
10from allauth.socialaccount.models import SocialAccount, SocialLogin
12from common.settings import get_global_setting
14logger = structlog.get_logger('inventree')
17def get_provider_app(provider):
18 """Return the SocialApp object for the given provider."""
19 from allauth.socialaccount.models import SocialApp
21 try:
22 apps = SocialApp.objects.filter(provider__iexact=provider.id)
23 except SocialApp.DoesNotExist:
24 logger.warning("SSO SocialApp not found for provider '%s'", provider.id)
25 return None
27 if apps.count() > 1:
28 logger.warning("Multiple SocialApps found for provider '%s'", provider.id)
30 if apps.count() == 0:
31 logger.warning("SSO SocialApp not found for provider '%s'", provider.id)
33 return apps.first()
36def check_provider(provider):
37 """Check if the given provider is correctly configured.
39 To be correctly configured, the following must be true:
41 - Provider must either have a registered SocialApp
42 - Must have at least one site enabled
43 """
44 import allauth.app_settings
46 # First, check that the provider is enabled
47 app = get_provider_app(provider)
49 if not app:
50 return False
52 if allauth.app_settings.SITES_ENABLED:
53 # At least one matching site must be specified
54 if not app.sites.exists():
55 logger.error('SocialApp %s has no sites configured', app)
56 return False
58 # At this point, we assume that the provider is correctly configured
59 return True
62def provider_display_name(provider):
63 """Return the 'display name' for the given provider."""
64 if app := get_provider_app(provider):
65 return app.name
67 # Fallback value if app not found
68 return provider.name
71def ensure_sso_groups(sender, sociallogin: SocialLogin, **kwargs):
72 """Sync groups from IdP each time a SSO user logs on.
74 This event listener is registered in the apps ready method.
75 """
76 if not get_global_setting('LOGIN_ENABLE_SSO_GROUP_SYNC'):
77 return
79 group_key = get_global_setting('SSO_GROUP_KEY')
80 group_map = json.loads(get_global_setting('SSO_GROUP_MAP'))
81 # map SSO groups to InvenTree groups
82 group_names = []
83 for sso_group in sociallogin.account.extra_data.get('userinfo', {}).get(
84 group_key, []
85 ):
86 if mapped_name := group_map.get(sso_group):
87 group_names.append(mapped_name)
89 # ensure user has groups
90 user = sociallogin.account.user
92 for group_name in group_names:
93 try:
94 user.groups.get(name=group_name)
95 except Group.DoesNotExist:
96 # user not in group yet
97 try:
98 group = Group.objects.get(name=group_name)
99 except Group.DoesNotExist:
100 logger.info(f'Creating group {group_name} as it did not exist')
101 group = Group(name=group_name)
102 group.save()
103 logger.info(f'Adding group {group_name} to user {user}')
104 user.groups.add(group)
106 # remove groups not listed by SSO if not disabled
107 if get_global_setting('SSO_REMOVE_GROUPS'):
108 for group in user.groups.all():
109 if group.name not in group_names:
110 logger.info(f'Removing group {group.name} from {user}')
111 user.groups.remove(group)
114@receiver(post_save, sender=SocialAccount)
115def on_social_account_created(sender, instance: SocialAccount, created: bool, **kwargs):
116 """Sync SSO groups when new SocialAccount is added.
118 Since the allauth `social_account_added` signal is not sent for some reason, this
119 signal is simulated using post_save signals. The issue has been reported as
120 https://github.com/pennersr/django-allauth/issues/3834
121 """
122 if created:
123 ensure_sso_groups(None, SocialLogin(account=instance))