added feature to grant access permissions via SSO groups
This commit is contained in:
parent
09b2375d79
commit
b5fbe28146
5 changed files with 102 additions and 0 deletions
|
@ -5,6 +5,7 @@ from django.urls import reverse
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from c3nav.control.models import UserPermissions
|
from c3nav.control.models import UserPermissions
|
||||||
|
from c3nav.mapdata.models.access import AccessPermissionSSOGrant
|
||||||
|
|
||||||
|
|
||||||
class UserPermissionsInline(admin.StackedInline):
|
class UserPermissionsInline(admin.StackedInline):
|
||||||
|
@ -27,3 +28,8 @@ class UserAdmin(BaseUserAdmin):
|
||||||
|
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(AccessPermissionSSOGrant)
|
||||||
|
class AccessPermissionSSOGrantAdmin(admin.ModelAdmin):
|
||||||
|
model = AccessPermissionSSOGrant
|
||||||
|
|
29
src/c3nav/control/sso/pipeline.py
Normal file
29
src/c3nav/control/sso/pipeline.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
from c3nav.mapdata.models.access import AccessPermission, AccessPermissionSSOGrant
|
||||||
|
|
||||||
|
|
||||||
|
def access_permissions(backend, response, user=None, *args, **kwargs):
|
||||||
|
"""Grant access permissions using group membership from provider."""
|
||||||
|
if not user or (groups := response.get('groups')) is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# delete permissions granted to the user by groups they no longer are part of
|
||||||
|
user.accesspermissions.filter(sso_grant__provider=backend.name).exclude(sso_grant__group__in=groups).delete()
|
||||||
|
|
||||||
|
if not groups:
|
||||||
|
return
|
||||||
|
|
||||||
|
existing_grants = set(AccessPermission.objects.filter(sso_grant__provider=backend.name)
|
||||||
|
.values_list('sso_grant_id', flat=True))
|
||||||
|
new_grants = AccessPermissionSSOGrant.objects.filter(provider=backend.name, group__in=groups) \
|
||||||
|
.exclude(id__in=existing_grants)
|
||||||
|
|
||||||
|
new_perms = []
|
||||||
|
for grant in new_grants:
|
||||||
|
new_perms.append(AccessPermission(
|
||||||
|
user=user,
|
||||||
|
access_restriction_id=grant.access_restriction_id,
|
||||||
|
access_restriction_group_id=grant.access_restriction_group_id,
|
||||||
|
sso_grant=grant
|
||||||
|
))
|
||||||
|
if new_grants:
|
||||||
|
AccessPermission.objects.bulk_create(new_perms)
|
|
@ -0,0 +1,42 @@
|
||||||
|
# Generated by Django 5.0.8 on 2024-09-12 21:22
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mapdata', '0108_in_legend'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AccessPermissionSSOGrant',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('provider', models.CharField(max_length=32, verbose_name='SSO Backend')),
|
||||||
|
('group', models.CharField(max_length=64, verbose_name='SSO Group')),
|
||||||
|
('access_restriction', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mapdata.accessrestriction')),
|
||||||
|
('access_restriction_group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mapdata.accessrestrictiongroup')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Access Permission SSO Grant',
|
||||||
|
'verbose_name_plural': 'Access Permission SSO Grants',
|
||||||
|
'default_related_name': 'accesspermission_sso_grants',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='accesspermission',
|
||||||
|
name='sso_grant',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='mapdata.accesspermissionssogrant', verbose_name='Access Permission SSO Grant'),
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='accesspermissionssogrant',
|
||||||
|
constraint=models.CheckConstraint(check=models.Q(models.Q(('access_restriction__isnull', True), ('access_restriction_group__isnull', True), _negated=True), models.Q(('access_restriction__isnull', False), ('access_restriction_group__isnull', False), _negated=True)), name='sso_permission_grant_needs_restriction_or_restriction_group'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='accesspermissionssogrant',
|
||||||
|
unique_together={('provider', 'group', 'access_restriction', 'access_restriction_group')},
|
||||||
|
),
|
||||||
|
]
|
|
@ -187,6 +187,28 @@ class AccessPermissionToken(models.Model):
|
||||||
return ngettext_lazy('Area successfully unlocked.', 'Areas successfully unlocked.', len(self.restrictions))
|
return ngettext_lazy('Area successfully unlocked.', 'Areas successfully unlocked.', len(self.restrictions))
|
||||||
|
|
||||||
|
|
||||||
|
class AccessPermissionSSOGrant(models.Model):
|
||||||
|
|
||||||
|
provider = models.CharField(max_length=32, verbose_name=_('SSO Backend'))
|
||||||
|
group = models.CharField(max_length=64, verbose_name=_('SSO Group'))
|
||||||
|
access_restriction = models.ForeignKey(AccessRestriction, on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
access_restriction_group = models.ForeignKey(AccessRestrictionGroup, on_delete=models.CASCADE, null=True,
|
||||||
|
blank=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Access Permission SSO Grant')
|
||||||
|
verbose_name_plural = _('Access Permission SSO Grants')
|
||||||
|
default_related_name = 'accesspermission_sso_grants'
|
||||||
|
unique_together = (
|
||||||
|
('provider', 'group', 'access_restriction', 'access_restriction_group')
|
||||||
|
)
|
||||||
|
constraints = (
|
||||||
|
CheckConstraint(check=(~Q(access_restriction__isnull=True, access_restriction_group__isnull=True) &
|
||||||
|
~Q(access_restriction__isnull=False, access_restriction_group__isnull=False)),
|
||||||
|
name="sso_permission_grant_needs_restriction_or_restriction_group"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AccessPermission(models.Model):
|
class AccessPermission(models.Model):
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)
|
||||||
session_token = models.UUIDField(null=True, editable=False)
|
session_token = models.UUIDField(null=True, editable=False)
|
||||||
|
@ -199,6 +221,8 @@ class AccessPermission(models.Model):
|
||||||
unique_key = models.CharField(max_length=32, null=True, verbose_name=_('unique key'))
|
unique_key = models.CharField(max_length=32, null=True, verbose_name=_('unique key'))
|
||||||
token = models.ForeignKey(AccessPermissionToken, null=True, on_delete=models.CASCADE,
|
token = models.ForeignKey(AccessPermissionToken, null=True, on_delete=models.CASCADE,
|
||||||
verbose_name=_('Access permission token'))
|
verbose_name=_('Access permission token'))
|
||||||
|
sso_grant = models.ForeignKey(AccessPermissionSSOGrant, null=True, on_delete=models.CASCADE,
|
||||||
|
verbose_name=_('Access Permission SSO Grant'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Access Permission')
|
verbose_name = _('Access Permission')
|
||||||
|
|
|
@ -809,4 +809,5 @@ SOCIAL_AUTH_PIPELINE = (
|
||||||
'social_core.pipeline.social_auth.associate_user',
|
'social_core.pipeline.social_auth.associate_user',
|
||||||
'social_core.pipeline.social_auth.load_extra_data',
|
'social_core.pipeline.social_auth.load_extra_data',
|
||||||
'social_core.pipeline.user.user_details',
|
'social_core.pipeline.user.user_details',
|
||||||
|
'c3nav.control.sso.pipeline.access_permissions',
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue