nettoyage des applications obsolètes/squash des migrations (#40685) #219

Open
bdauvergne wants to merge 19 commits from wip/40685-squash-des-migrations into main
160 changed files with 3897 additions and 3530 deletions

View File

@ -6,6 +6,7 @@ set -e
CHECK_MIGRATIONS_SETTINGS=`mktemp`
trap "rm -f ${CHECK_MIGRATIONS_SETTINGS}" EXIT
cat <<EOF >${CHECK_MIGRATIONS_SETTINGS}
LANGUAGE_CODE = 'en'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.dummy',

View File

@ -18,7 +18,6 @@ import contextlib
import datetime
import threading
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.db import connection, models
from django.db.models import query
@ -167,7 +166,8 @@ class RoleQuerySet(query.QuerySet):
return qs
def all_members(self):
User = get_user_model()
from authentic2.custom_user.models import User
prefetch = Prefetch('roles', queryset=self, to_attr='direct')
return (
User.objects.filter(

View File

@ -6,12 +6,7 @@ import authentic2.utils.misc
class Migration(migrations.Migration):
dependencies = [
('authentic2', '0004_service'),
('django_rbac', '__first__'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contenttypes', '0001_initial'),
]
dependencies = []
operations = [
migrations.CreateModel(
@ -118,7 +113,7 @@ class Migration(migrations.Migration):
),
(
'members',
models.ManyToManyField(related_name='roles', to=settings.AUTH_USER_MODEL, blank=True),
models.ManyToManyField(related_name='roles', to='auth.User', blank=True),
),
(
'ou',

View File

@ -2,36 +2,10 @@
from django.db import migrations
from authentic2.a2_rbac.models import CHANGE_OP, MANAGE_MEMBERS_OP
def update_self_administration_perm(apps, schema_editor):
Role = apps.get_model('a2_rbac', 'Role')
Permission = apps.get_model('a2_rbac', 'Permission')
Operation = apps.get_model('django_rbac', 'Operation')
ContentType = apps.get_model('contenttypes', 'ContentType')
change_op, _ = Operation.objects.get_or_create(slug=str(CHANGE_OP.slug))
manage_members_op, _ = Operation.objects.get_or_create(slug=str(MANAGE_MEMBERS_OP.slug))
ct = ContentType.objects.get_for_model(Role)
perms_to_delete = []
for role in Role.objects.all():
try:
perm = role.permissions.get(operation=change_op, target_ct=ct, target_id=role.pk)
except Permission.DoesNotExist:
continue
new_perm, _ = Permission.objects.get_or_create(
operation=manage_members_op, target_ct=ct, target_id=role.pk, ou__isnull=True
)
role.permissions.add(new_perm)
role.permissions.remove(perm)
perms_to_delete.append(perm.pk)
Permission.objects.filter(pk__in=perms_to_delete, roles__isnull=True).delete()
class Migration(migrations.Migration):
dependencies = [
('a2_rbac', '0023_role_can_manage_members'),
]
operations = [migrations.RunPython(update_self_administration_perm, migrations.RunPython.noop)]
operations = []

View File

@ -7,8 +7,6 @@ from authentic2.migrations import DropPartialIndexes
class Migration(migrations.Migration):
dependencies = [
('authentic2', '0039_add_unique_attribute_constraint'),
('contenttypes', '0002_remove_content_type_name'),
('a2_rbac', '0028_ou_home_url'),
('a2_rbac', '0027_auto_20220331_1521'),
]

View File

@ -3,40 +3,9 @@
from django.db import migrations
def copy_operations_data(apps, schema_editor):
OldOperation = apps.get_model('django_rbac', 'Operation')
NewOperation = apps.get_model('a2_rbac', 'Operation')
Permission = apps.get_model('a2_rbac', 'Permission')
operation_map = {}
for operation in OldOperation.objects.all():
operation_map[operation.pk] = NewOperation.objects.create(slug=operation.slug)
for permission in Permission.objects.all():
permission.operation_new = operation_map[permission.operation_id]
permission.save()
def reverse_copy_operations_data(apps, schema_editor):
OldOperation = apps.get_model('django_rbac', 'Operation')
NewOperation = apps.get_model('a2_rbac', 'Operation')
Permission = apps.get_model('a2_rbac', 'Permission')
operation_map = {}
for operation in NewOperation.objects.all():
operation_map[operation.pk] = OldOperation.objects.create(slug=operation.slug)
for permission in Permission.objects.all():
permission.operation = operation_map[permission.operation_new_id]
permission.save()
class Migration(migrations.Migration):
dependencies = [
('a2_rbac', '0031_new_operation_model'),
('django_rbac', '0009_auto_20221004_1343'),
]
operations = [
migrations.RunPython(copy_operations_data, reverse_code=reverse_copy_operations_data),
]
operations = []

View File

@ -6,7 +6,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('a2_rbac', '0036_delete_roleattribute'),
('authenticators', '0013_migrate_min_password_strength'),
]
operations = [

View File

@ -0,0 +1,289 @@
# Generated by Django 3.2.23 on 2023-12-14 21:01
import django.contrib.postgres.fields
import django.core.validators
import django.db.models.deletion
from django.db import migrations, models
import authentic2.a2_rbac.fields
import authentic2.a2_rbac.utils
import authentic2.validators
class Migration(migrations.Migration):
replaces = [
('a2_rbac', '0001_initial'),
('a2_rbac', '0002_role_external_id'),
('a2_rbac', '0003_partial_unique_index_on_name_and_slug'),
('a2_rbac', '0004_auto_20150523_0028'),
('a2_rbac', '0005_auto_20150526_1406'),
('a2_rbac', '0006_auto_20150619_1056'),
('a2_rbac', '0007_auto_20150708_1337'),
('a2_rbac', '0008_auto_20150810_1953'),
('a2_rbac', '0009_partial_unique_index_on_permission'),
('a2_rbac', '0010_auto_20160209_1417'),
('a2_rbac', '0011_auto_20160209_1511'),
('a2_rbac', '0013_auto_20170629_0007'),
('a2_rbac', '0014_auto_20170711_1024'),
('a2_rbac', '0015_organizationalunit_validate_emails'),
('a2_rbac', '0016_auto_20171208_1429'),
('a2_rbac', '0017_organizationalunit_user_can_reset_password'),
('a2_rbac', '0018_organizationalunit_user_add_password_policy'),
('a2_rbac', '0019_organizationalunit_show_username'),
('a2_rbac', '0020_partial_unique_index_on_name'),
('a2_rbac', '0021_auto_20200317_1514'),
('a2_rbac', '0022_auto_20200402_1101'),
('a2_rbac', '0023_role_can_manage_members'),
('a2_rbac', '0024_fix_self_admin_perm'),
('a2_rbac', '0025_auto_20210622_1132'),
('a2_rbac', '0026_add_roleparenting_soft_delete'),
('a2_rbac', '0026_organizationalunit_check_required_on_login_attributes'),
('a2_rbac', '0027_auto_20211213_0949'),
('a2_rbac', '0027_auto_20220331_1521'),
('a2_rbac', '0028_ou_home_url'),
('a2_rbac', '0029_use_unique_constraints'),
('a2_rbac', '0030_organizationalunit_min_password_strength'),
('a2_rbac', '0031_new_operation_model'),
('a2_rbac', '0032_copy_operations_data'),
('a2_rbac', '0033_remove_old_operation_fk'),
('a2_rbac', '0034_new_role_fields'),
('a2_rbac', '0035_populate_role_fields'),
('a2_rbac', '0036_delete_roleattribute'),
]
initial = True
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
]
operations = [
migrations.CreateModel(
name='Operation',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('slug', models.CharField(max_length=32, unique=True, verbose_name='slug')),
],
),
migrations.CreateModel(
name='OrganizationalUnit',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
(
'uuid',
models.CharField(
default=authentic2.a2_rbac.utils.get_hex_uuid,
max_length=32,
unique=True,
verbose_name='uuid',
),
),
('name', models.CharField(max_length=256, verbose_name='name')),
('slug', models.SlugField(max_length=256, verbose_name='slug')),
('description', models.TextField(blank=True, verbose_name='description')),
(
'username_is_unique',
models.BooleanField(blank=True, default=False, verbose_name='Username is unique'),
),
(
'email_is_unique',
models.BooleanField(blank=True, default=False, verbose_name='Email is unique'),
),
(
'default',
authentic2.a2_rbac.fields.UniqueBooleanField(verbose_name='Default organizational unit'),
),
(
'validate_emails',
models.BooleanField(blank=True, default=False, verbose_name='Validate emails'),
),
(
'show_username',
models.BooleanField(blank=True, default=True, verbose_name='Show username'),
),
(
'check_required_on_login_attributes',
models.BooleanField(
blank=True, default=True, verbose_name='Check required on login attributes'
),
),
(
'user_can_reset_password',
models.BooleanField(
blank=True,
choices=[(None, 'System default'), (True, 'Yes'), (False, 'No')],
default=None,
null=True,
verbose_name='Users can reset password',
),
),
(
'user_add_password_policy',
models.IntegerField(
choices=[(0, 'Send reset link'), (1, 'Manual password definition')],
default=0,
verbose_name='User creation password policy',
),
),
(
'clean_unused_accounts_alert',
models.PositiveIntegerField(
blank=True,
default=730,
null=True,
validators=[
django.core.validators.MinValueValidator(
30,
'Ensure that this value is greater than 30 days, or leave blank for deactivating.',
)
],
verbose_name='Days after which the user receives an account deletion alert',
),
),
(
'clean_unused_accounts_deletion',
models.PositiveIntegerField(
blank=True,
default=760,
null=True,
validators=[
django.core.validators.MinValueValidator(
30,
'Ensure that this value is greater than 30 days, or leave blank for deactivating.',
)
],
verbose_name='Delay in days before cleaning unused accounts',
),
),
('home_url', models.URLField(blank=True, max_length=256, null=True, verbose_name='Home URL')),
('logo', models.ImageField(blank=True, upload_to='services/logos', verbose_name='Logo')),
(
'colour',
models.CharField(
blank=True,
max_length=32,
null=True,
validators=[authentic2.validators.HexaColourValidator()],
verbose_name='Colour',
),
),
(
'phone_is_unique',
models.BooleanField(blank=True, default=False, verbose_name='Phone is unique'),
),
],
options={
'verbose_name': 'organizational unit',
'verbose_name_plural': 'organizational units',
'ordering': ('name',),
},
),
migrations.CreateModel(
name='Permission',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('target_id', models.PositiveIntegerField()),
],
options={
'verbose_name': 'permission',
'verbose_name_plural': 'permissions',
},
),
migrations.CreateModel(
name='Role',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
(
'uuid',
models.CharField(
default=authentic2.a2_rbac.utils.get_hex_uuid,
max_length=32,
unique=True,
verbose_name='uuid',
),
),
('slug', models.SlugField(max_length=256, verbose_name='slug')),
('description', models.TextField(blank=True, verbose_name='description')),
('name', models.TextField(verbose_name='name')),
('details', models.TextField(blank=True, verbose_name='Role details (frontoffice)')),
(
'emails',
django.contrib.postgres.fields.ArrayField(
base_field=models.EmailField(max_length=254), default=list, size=None
),
),
('emails_to_members', models.BooleanField(default=True, verbose_name='Emails to members')),
('is_superuser', models.BooleanField(default=False)),
(
'admin_scope_id',
models.PositiveIntegerField(
blank=True, null=True, verbose_name='administrative scope id'
),
),
('external_id', models.TextField(blank=True, db_index=True, verbose_name='external id')),
(
'can_manage_members',
models.BooleanField(default=True, verbose_name='Allow adding or deleting role members'),
),
(
'admin_scope_ct',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to='contenttypes.contenttype',
verbose_name='administrative scope content type',
),
),
],
options={
'verbose_name': 'role',
'verbose_name_plural': 'roles',
'ordering': ('ou', 'service', 'name'),
},
),
migrations.CreateModel(
name='RoleParenting',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('direct', models.BooleanField(blank=True, default=True)),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Creation date')),
('deleted', models.DateTimeField(null=True, verbose_name='Deletion date')),
(
'child',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='parent_relation',
to='a2_rbac.role',
),
),
(
'parent',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='child_relation',
to='a2_rbac.role',
),
),
],
options={
'verbose_name': 'role parenting relation',
'verbose_name_plural': 'role parenting relations',
},
),
]

View File

@ -0,0 +1,174 @@
# Generated by Django 3.2.23 on 2023-12-14 21:01
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
replaces = [
('a2_rbac', '0037_remove_organizationalunit_min_password_strength'),
('a2_rbac', '0038_organizationalunit_phone_is_unique'),
('a2_rbac', '0039_set_user_view_permissions_by_ou'),
]
initial = True
dependencies = [
('a2_rbac', '0040_replace'),
('custom_user', '0037_replace'),
('contenttypes', '0002_remove_content_type_name'),
('authentic2', '0051_replace'),
]
operations = [
migrations.AddField(
model_name='role',
name='members',
field=models.ManyToManyField(blank=True, related_name='roles', to='custom_user.User'),
),
migrations.AddField(
model_name='role',
name='ou',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to='a2_rbac.organizationalunit',
verbose_name='organizational unit',
),
),
migrations.AddField(
model_name='role',
name='permissions',
field=models.ManyToManyField(blank=True, related_name='roles', to='a2_rbac.Permission'),
),
migrations.AddField(
model_name='role',
name='service',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name='roles',
to='authentic2.service',
verbose_name='service',
),
),
migrations.AddField(
model_name='permission',
name='operation',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to='a2_rbac.operation', verbose_name='operation'
),
),
migrations.AddField(
model_name='permission',
name='ou',
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name='scoped_permission',
to='a2_rbac.organizationalunit',
verbose_name='organizational unit',
),
),
migrations.AddField(
model_name='permission',
name='target_ct',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, related_name='+', to='contenttypes.contenttype'
),
),
migrations.AlterUniqueTogether(
name='organizationalunit',
unique_together={('name',), ('slug',)},
),
migrations.AlterUniqueTogether(
name='roleparenting',
unique_together={('parent', 'child', 'direct')},
),
migrations.AlterIndexTogether(
name='roleparenting',
index_together={('child', 'parent', 'direct')},
),
migrations.AddConstraint(
model_name='role',
constraint=models.UniqueConstraint(
condition=models.Q(('admin_scope_ct__isnull', True)),
fields=('ou', 'service', 'slug'),
name='slug_uniq_idx',
),
),
migrations.AddConstraint(
model_name='role',
constraint=models.UniqueConstraint(
condition=models.Q(('admin_scope_ct__isnull', True)),
fields=('ou', 'service', 'name'),
name='name_uniq_idx',
),
),
migrations.AddConstraint(
model_name='role',
constraint=models.UniqueConstraint(
condition=models.Q(('admin_scope_ct__isnull', True), ('service__isnull', True)),
fields=('ou', 'slug'),
name='null_service_slug_uniq_idx',
),
),
migrations.AddConstraint(
model_name='role',
constraint=models.UniqueConstraint(
condition=models.Q(('admin_scope_ct__isnull', True), ('ou__isnull', True)),
fields=('service', 'slug'),
name='null_ou_slug_uniq_idx',
),
),
migrations.AddConstraint(
model_name='role',
constraint=models.UniqueConstraint(
condition=models.Q(
('admin_scope_ct__isnull', True), ('ou__isnull', True), ('service__isnull', True)
),
fields=('slug',),
name='null_ou_service_slug_uniq_idx',
),
),
migrations.AddConstraint(
model_name='role',
constraint=models.UniqueConstraint(
condition=models.Q(('admin_scope_ct__isnull', True), ('service__isnull', True)),
fields=('ou', 'name'),
name='null_service_name_uniq_idx',
),
),
migrations.AddConstraint(
model_name='role',
constraint=models.UniqueConstraint(
condition=models.Q(('admin_scope_ct__isnull', True), ('ou__isnull', True)),
fields=('service', 'name'),
name='null_ou_name_uniq_idx',
),
),
migrations.AddConstraint(
model_name='role',
constraint=models.UniqueConstraint(
condition=models.Q(
('admin_scope_ct__isnull', True), ('ou__isnull', True), ('service__isnull', True)
),
fields=('name',),
name='null_ou_service_name_uniq_idx',
),
),
migrations.AlterUniqueTogether(
name='role',
unique_together={('admin_scope_ct', 'admin_scope_id')},
),
migrations.AddConstraint(
model_name='permission',
constraint=models.UniqueConstraint(
condition=models.Q(('ou__isnull', True)),
fields=('operation', 'target_ct', 'target_id'),
name='null_ou_uniq_idx',
),
),
]

View File

@ -19,8 +19,6 @@ import os
from collections import namedtuple
from django.apps import apps
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import ArrayField
@ -363,9 +361,7 @@ class Role(AbstractBase):
null=True,
on_delete=models.CASCADE,
)
members = models.ManyToManyField(
to=settings.AUTH_USER_MODEL, swappable=True, blank=True, related_name='roles'
)
members = models.ManyToManyField(to='custom_user.User', swappable=True, blank=True, related_name='roles')
permissions = models.ManyToManyField(to=Permission, related_name='roles', blank=True)
name = models.TextField(verbose_name=_('name'))
details = models.TextField(_('Role details (frontoffice)'), blank=True)
@ -426,7 +422,8 @@ class Role(AbstractBase):
)
def all_members(self):
User = get_user_model()
from authentic2.custom_user.models import User
prefetch = Prefetch('roles', queryset=self.__class__.objects.filter(pk=self.pk), to_attr='direct')
return (

View File

@ -16,7 +16,6 @@
import uuid
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.utils.text import slugify
@ -44,7 +43,8 @@ def get_default_ou_pk():
def get_view_user_perm(ou=None):
User = get_user_model()
from authentic2.custom_user.models import User
view_user_perm, dummy = models.Permission.objects.get_or_create(
operation=get_operation(models.VIEW_OP),
target_ct=ContentType.objects.get_for_model(ContentType),
@ -74,7 +74,8 @@ def get_search_ou_perm(ou=None):
def get_manage_authorizations_user_perm(ou=None):
User = get_user_model()
from authentic2.custom_user.models import User
manage_authorizations_user_perm, dummy = models.Permission.objects.get_or_create(
operation=get_operation(models.MANAGE_AUTHORIZATIONS_OP),
target_ct=ContentType.objects.get_for_model(ContentType),

View File

@ -32,7 +32,6 @@ from django.views.decorators.cache import never_cache
from . import app_settings, attribute_kinds, decorators, models
from .custom_user.models import DeletedUser, Profile, ProfileType, User
from .forms.profile import BaseUserForm, modelform_factory
from .nonce.models import Nonce
from .utils import misc as utils_misc
@ -49,11 +48,6 @@ class CleanupAdminMixin(admin.ModelAdmin):
return actions
@admin.register(Nonce)
class NonceModelAdmin(admin.ModelAdmin):
list_display = ('value', 'context', 'not_on_or_after')
class AttributeValueAdmin(admin.ModelAdmin):
list_display = ('content_type', 'owner', 'attribute', 'content')

View File

@ -21,7 +21,6 @@ from functools import partial
import requests
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.hashers import identify_hasher
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import MultipleObjectsReturned
@ -75,8 +74,6 @@ from .utils import misc as utils_misc
from .utils.api import DjangoRBACPermission, NaturalKeyRelatedField
from .utils.lookups import Unaccent
User = get_user_model()
class HookMixin:
def get_serializer(self, *args, **kwargs):
@ -153,7 +150,6 @@ class RegistrationSerializer(serializers.Serializer):
authorized = request.user.has_perm(perm)
if not authorized:
raise serializers.ValidationError(_('you are not authorized to create users in this ou'))
User = get_user_model()
if ou:
if app_settings.A2_EMAIL_IS_UNIQUE or app_settings.A2_REGISTRATION_EMAIL_IS_UNIQUE:
if 'email' not in attrs:
@ -207,7 +203,6 @@ class PasswordChangeSerializer(serializers.Serializer):
new_password = serializers.CharField(required=True, allow_null=True)
def validate(self, attrs):
User = get_user_model()
qs = User.objects.filter(email__iexact=attrs['email'])
if attrs['ou']:
qs = qs.filter(ou=attrs['ou'])
@ -394,7 +389,6 @@ class BaseUserSerializer(serializers.ModelSerializer):
return instance
def validate(self, attrs):
User = get_user_model()
qs = User.objects.all()
ou = None
@ -439,7 +433,7 @@ class BaseUserSerializer(serializers.ModelSerializer):
return attrs
class Meta:
model = get_user_model()
model = User
extra_kwargs = {
'uuid': {
'read_only': False,
@ -556,7 +550,7 @@ class IsoDateTimeFilter(BaseIsoDateTimeFilter):
class UsersFilter(FilterSet):
class Meta:
model = get_user_model()
model = User
fields = {
'username': ['exact', 'iexact'],
'first_name': [
@ -721,7 +715,7 @@ class UsersAPI(api_mixins.GetOrCreateMixinView, HookMixin, ExceptionHandlerMixin
def check_modified_uuids(self, timestamp, users, unknown_uuids):
modified_users_uuids = set()
user_ct = ContentType.objects.get_for_model(get_user_model())
user_ct = ContentType.objects.get_for_model(User)
reference_ids = [reference_integer(user) for user in users]
user_events = Event.objects.filter(
models.Q(reference_ids__overlap=reference_ids) | models.Q(user__in=users),
@ -999,7 +993,6 @@ class UserProfilesAPI(ExceptionHandlerMixin, APIView):
def initial(self, request, *args, **kwargs):
super().initial(request, *args, **kwargs)
User = get_user_model()
self.profile_type = get_object_or_404(ProfileType, slug=kwargs['profile_type_slug'])
self.user = get_object_or_404(User, uuid=kwargs['user_uuid'])
self.identifier = request.GET.get('identifier', '')
@ -1175,7 +1168,6 @@ class RoleMembershipsAPI(ExceptionHandlerMixin, APIView):
def initial(self, request, *, role_uuid=None, role_slug=None, **kwargs):
super().initial(request, role_uuid=role_uuid, role_slug=role_slug, **kwargs)
User = get_user_model()
if role_uuid:
self.role = get_object_or_404(Role, uuid=role_uuid)
if role_slug:

View File

@ -0,0 +1,5 @@
DEFAULT_MIN_PASSWORD_STRENGTH = 3
def get_default_min_password_strength():
return DEFAULT_MIN_PASSWORD_STRENGTH

View File

@ -12,10 +12,7 @@ import authentic2.utils.evaluate
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.RBAC_OU_MODEL),
('a2_rbac', '0036_delete_roleattribute'),
]
dependencies = []
operations = [
migrations.CreateModel(

View File

@ -7,9 +7,7 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.RBAC_ROLE_MODEL),
('authenticators', '0004_auto_20220726_1708'),
('authentic2_auth_saml', '0012_move_add_role_action'),
]
state_operations = [

View File

@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentic2', '0048_rename_services_runtime_settings'),
('authenticators', '0009_migrate_new_password_settings'),
]

View File

@ -35,7 +35,6 @@ def migrate_min_password_strength(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [
('authenticators', '0012_loginpasswordauthenticator_min_password_strength'),
('a2_rbac', '0036_delete_roleattribute'),
]
operations = [

View File

@ -0,0 +1,325 @@
# Generated by Django 3.2.23 on 2023-12-14 21:01
import uuid
import django.db.models.deletion
from django.db import migrations, models
import authentic2.utils.evaluate
import authentic2.utils.template
from authentic2.apps.authenticators import get_default_min_password_strength
def create_login_password_authenticator(apps, schema_editor):
LoginPasswordAuthenticator = apps.get_model('authenticators', 'LoginPasswordAuthenticator')
LoginPasswordAuthenticator.objects.create(slug='password-authenticator', enabled=True)
class Migration(migrations.Migration):
replaces = [
('authenticators', '0001_initial'),
('authenticators', '0002_loginpasswordauthenticator'),
('authenticators', '0003_auto_20220413_1504'),
('authenticators', '0004_auto_20220726_1708'),
('authenticators', '0005_addroleaction'),
('authenticators', '0006_loginpasswordauthenticator_registration_open'),
('authenticators', '0007_migrate_registration_open'),
('authenticators', '0008_new_password_settings_fields'),
('authenticators', '0009_migrate_new_password_settings'),
('authenticators', '0010_auto_20230614_1017'),
('authenticators', '0011_migrate_a2_accept_authentication_settings'),
('authenticators', '0012_loginpasswordauthenticator_min_password_strength'),
('authenticators', '0013_migrate_min_password_strength'),
('authenticators', '0014_auto_20230801_1517'),
('authenticators', '0015_alter_baseauthenticator_button_label'),
('authenticators', '0016_alter_addroleaction_condition'),
('authenticators', '0017_auto_20230927_1517'),
('authenticators', '0018_auto_20230927_1519'),
('authenticators', '0019_fix_addroleaction_condition'),
]
initial = True
dependencies = [
('a2_rbac', '0040_replace'),
('authentic2', '0051_replace'),
]
operations = [
migrations.CreateModel(
name='BaseAuthenticator',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('uuid', models.CharField(default=uuid.uuid4, editable=False, max_length=255, unique=True)),
('name', models.CharField(blank=True, max_length=128, verbose_name='Name')),
('slug', models.SlugField(unique=True)),
('order', models.IntegerField(default=0, editable=False, verbose_name='Order')),
('enabled', models.BooleanField(default=False, editable=False)),
(
'show_condition',
models.CharField(
blank=True,
default='',
help_text='Condition controlling authenticator display. For example, "\'backoffice\' in '
'login_hint or remote_addr == \'1.2.3.4\'" would hide the authenticator from normal users '
'except if they come from the specified IP address. Available variables include '
'service_ou_slug, service_slug, remote_addr, login_hint and headers.',
max_length=1024,
validators=[authentic2.utils.evaluate.condition_validator],
verbose_name='Show condition',
),
),
(
'button_description',
models.CharField(
blank=True,
help_text='Description will be shown at the top of login block (unless already set by theme).',
max_length=256,
verbose_name='Login block description',
),
),
(
'button_label',
models.CharField(default='Login', max_length=256, verbose_name='Login button label'),
),
(
'ou',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to='a2_rbac.organizationalunit',
verbose_name='organizational unit',
),
),
],
options={
'ordering': ('-enabled', 'order', 'name', 'slug', 'ou'),
},
),
migrations.CreateModel(
name='AddRoleAction',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
(
'mandatory',
models.BooleanField(default=False, editable=False, verbose_name='Mandatory (unused)'),
),
(
'condition',
models.CharField(
blank=True,
default='',
help_text='Django condition controlling role attribution. For example, "\'Admin\' in attributes.groups" '
'will attribute the role if attributes has "groups" attribute containing the value "Admin". Variable "attributes" '
'contains the attributes received from the identity provider. If condition is not satisfied the role will be removed.',
max_length=1024,
validators=[authentic2.utils.template.validate_condition_template],
verbose_name='Condition',
),
),
(
'authenticator',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='add_role_actions',
to='authenticators.baseauthenticator',
),
),
(
'role',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='add_role_actions',
to='a2_rbac.role',
verbose_name='Role',
),
),
],
options={
'verbose_name': 'Add a role',
'verbose_name_plural': 'Add roles',
'default_related_name': 'add_role_actions',
},
),
migrations.CreateModel(
name='LoginPasswordAuthenticator',
fields=[
(
'baseauthenticator_ptr',
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to='authenticators.baseauthenticator',
),
),
(
'registration_open',
models.BooleanField(
default=True,
help_text='Allow users to create accounts.',
verbose_name='Registration open',
),
),
(
'remember_me',
models.PositiveIntegerField(
blank=True,
help_text='Session duration as seconds when using the remember me checkbox. Leave blank to hide the checkbox.',
null=True,
verbose_name='Remember me duration',
),
),
(
'include_ou_selector',
models.BooleanField(default=False, verbose_name='Include OU selector in login form'),
),
(
'accept_email_authentication',
models.BooleanField(
default=True, verbose_name='Let the users identify with their email address'
),
),
(
'accept_phone_authentication',
models.BooleanField(
default=False, verbose_name='Let the users identify with their phone number'
),
),
(
'min_password_strength',
models.IntegerField(
blank=True,
choices=[
(None, 'Follow static checks'),
(0, 'Very Weak'),
(1, 'Weak'),
(2, 'Fair'),
(3, 'Good'),
(4, 'Strong'),
],
default=get_default_min_password_strength,
help_text='Password strength, using dynamic indicators such as common names, dates and other popular patterns. Selecting '
'"static checks" will instead validate that a password contains enough different kind of caracters. Password indicator on '
'registration form will reflect the chosen policy.',
null=True,
verbose_name='Minimum password strength',
),
),
(
'password_min_length',
models.PositiveIntegerField(default=8, null=True, verbose_name='Password minimum length'),
),
(
'password_regex',
models.CharField(
blank=True,
default='',
max_length=512,
verbose_name='Regular expression for validating passwords',
),
),
(
'password_regex_error_msg',
models.CharField(
blank=True,
default='',
max_length=1024,
verbose_name='Error message to show when the password do not validate the regular expression',
),
),
(
'login_exponential_retry_timeout_duration',
models.FloatField(
default=1,
help_text='Exponential backoff base factor duration as seconds until next try after a login failure.',
verbose_name='Retry timeout duration',
),
),
(
'login_exponential_retry_timeout_factor',
models.FloatField(
default=1.8,
help_text='Exponential backoff factor duration as seconds until next try after a login failure.',
verbose_name='Retry timeout factor',
),
),
(
'login_exponential_retry_timeout_max_duration',
models.PositiveIntegerField(
default=3600,
help_text='Maximum exponential backoff maximum duration as seconds until next try after a login failure.',
verbose_name='Retry timeout max duration',
),
),
(
'login_exponential_retry_timeout_min_duration',
models.PositiveIntegerField(
default=10,
help_text='Minimum exponential backoff maximum duration as seconds until next try after a login failure.',
verbose_name='Retry timeout min duration',
),
),
(
'emails_ip_ratelimit',
models.CharField(
default='10/h',
help_text='Maximum rate of email sendings triggered by the same IP address.',
max_length=32,
verbose_name='Emails IP ratelimit',
),
),
(
'sms_ip_ratelimit',
models.CharField(
default='10/h',
help_text='Maximum rate of SMSs triggered by the same IP address.',
max_length=32,
verbose_name='SMS IP ratelimit',
),
),
(
'emails_address_ratelimit',
models.CharField(
default='3/d',
help_text='Maximum rate of emails sent to the same email address.',
max_length=32,
verbose_name='Emails address ratelimit',
),
),
(
'sms_number_ratelimit',
models.CharField(
default='10/h',
help_text='Maximum rate of SMSs sent to the same phone number.',
max_length=32,
verbose_name='SMS number ratelimit',
),
),
(
'phone_identifier_field',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
to='authentic2.attribute',
verbose_name='Phone field used as user identifier',
),
),
],
options={
'verbose_name': 'Password',
},
bases=('authenticators.baseauthenticator',),
),
migrations.RunPython(create_login_password_authenticator, reverse_code=migrations.RunPython.noop),
]

View File

@ -37,6 +37,7 @@ from authentic2.models import Attribute
from authentic2.utils.evaluate import condition_validator, evaluate_condition
from authentic2.utils.template import validate_condition_template
from . import get_default_min_password_strength
from .query import AuthenticatorManager
logger = logging.getLogger(__name__)
@ -335,7 +336,7 @@ class LoginPasswordAuthenticator(BaseAuthenticator):
min_password_strength = models.IntegerField(
verbose_name=_('Minimum password strength'),
choices=MIN_PASSWORD_STRENGTH_CHOICES,
default=3,
default=get_default_min_password_strength,
blank=True,
null=True,
help_text=_(

View File

@ -3,7 +3,6 @@
import django
import django.contrib.postgres.fields
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
from django.db.models import JSONField
from django.utils import timezone
@ -12,11 +11,7 @@ from django.utils import timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('authentic2', '0027_remove_deleteduser'),
('sessions', '0001_initial'),
]
dependencies = []
operations = [
migrations.CreateModel(
@ -92,7 +87,7 @@ class Migration(migrations.Migration):
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
to=settings.AUTH_USER_MODEL,
to='custom_user.User',
verbose_name='user',
),
),

View File

@ -0,0 +1,108 @@
# Generated by Django 3.2.23 on 2023-12-14 21:01
import django.contrib.postgres.fields
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
replaces = [('journal', '0001_initial'), ('journal', '0002_event_api')]
initial = True
dependencies = [
('sessions', '0001_initial'),
('custom_user', '0037_replace'),
]
operations = [
migrations.CreateModel(
name='EventType',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('name', models.SlugField(max_length=256, unique=True, verbose_name='name')),
],
options={
'verbose_name': 'event type',
'verbose_name_plural': 'event types',
'ordering': ('name',),
},
),
migrations.CreateModel(
name='Event',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
(
'timestamp',
models.DateTimeField(
blank=True,
default=django.utils.timezone.now,
editable=False,
verbose_name='timestamp',
),
),
(
'reference_ids',
django.contrib.postgres.fields.ArrayField(
base_field=models.BigIntegerField(),
null=True,
size=None,
verbose_name='reference ids',
),
),
(
'reference_ct_ids',
django.contrib.postgres.fields.ArrayField(
base_field=models.IntegerField(),
null=True,
size=None,
verbose_name='reference ct ids',
),
),
('data', models.JSONField(null=True, verbose_name='data')),
('api', models.BooleanField(default=False, verbose_name='API')),
(
'session',
models.ForeignKey(
blank=True,
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
to='sessions.session',
verbose_name='session',
),
),
(
'type',
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to='journal.eventtype',
verbose_name='type',
),
),
(
'user',
models.ForeignKey(
blank=True,
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
to='custom_user.user',
verbose_name='user',
),
),
],
options={
'verbose_name': 'event',
'verbose_name_plural': 'events',
'ordering': ('timestamp', 'id'),
},
),
]

View File

@ -23,7 +23,6 @@ from contextlib import contextmanager
from datetime import datetime, timedelta
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ObjectDoesNotExist
@ -41,8 +40,6 @@ from .utils import Statistics
logger = logging.getLogger(__name__)
User = get_user_model()
_registry = {}
@ -87,6 +84,8 @@ class EventTypeDefinition(metaclass=EventTypeDefinitionMeta):
@classmethod
def record(cls, *, user=None, session=None, references=None, data=None, api=False):
from authentic2.custom_user.models import User
event_type = EventType.objects.get_for_name(cls.name)
if user and not isinstance(user, User):
@ -216,6 +215,8 @@ class EventType(models.Model):
class EventQuerySet(QuerySet):
@classmethod
def _which_references_query(cls, instance_or_model_class_or_queryset):
from authentic2.custom_user.models import User
if isinstance(instance_or_model_class_or_queryset, list):
return functools.reduce(
operator.or_,
@ -315,7 +316,7 @@ class Event(models.Model):
user = models.ForeignKey(
verbose_name=_('user'),
to=User,
to='custom_user.User',
on_delete=models.DO_NOTHING,
db_constraint=False,
blank=True,
@ -489,6 +490,8 @@ class EventCursor(str):
def prefetch_events_references(events, prefetcher=None):
'''Prefetch references on an iterable of events, prevent N+1 queries problem.'''
from authentic2.custom_user.models import User
grouped_references = defaultdict(set)
references = {}

View File

@ -17,13 +17,12 @@
import re
from functools import reduce
from django.contrib.auth import get_user_model
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from . import models
from authentic2.custom_user.models import User
User = get_user_model()
from . import models
QUOTED_RE = re.compile(r'^[a-z0-9_-]*:"[^"]*"$')
LEXER_RE = re.compile(r'([a-z0-9_-]*:(?:"[^"]*"|[^ ]*)|[^\s]*)\s*')

View File

@ -1,484 +0,0 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0002_auto_20150323_1720'),
]
operations = [
migrations.CreateModel(
name='AttributeItem',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
(
'attribute_name',
models.CharField(
default=('OpenLDAProotDSE', 'OpenLDAProotDSE'),
max_length=100,
verbose_name='Attribute name',
choices=[
('OpenLDAProotDSE', 'OpenLDAProotDSE'),
('aRecord', 'aRecord'),
('administrativeRole', 'administrativeRole'),
('alias', 'alias'),
('aliasedObjectName', 'aliasedObjectName'),
('altServer', 'altServer'),
('associatedDomain', 'associatedDomain'),
('associatedName', 'associatedName'),
('attributeTypes', 'attributeTypes'),
('audio', 'audio'),
('authPassword', 'authPassword'),
('authorityRevocationList', 'authorityRevocationList'),
('authzFrom', 'authzFrom'),
('authzTo', 'authzTo'),
('bootFile', 'bootFile'),
('bootParameter', 'bootParameter'),
('buildingName', 'buildingName'),
('businessCategory', 'businessCategory'),
('c', 'c'),
('cACertificate', 'cACertificate'),
('cNAMERecord', 'cNAMERecord'),
('carLicense', 'carLicense'),
('certificateRevocationList', 'certificateRevocationList'),
('children', 'children'),
('cn', 'cn'),
('co', 'co'),
('collectiveAttributeSubentries', 'collectiveAttributeSubentries'),
('collectiveAttributeSubentry', 'collectiveAttributeSubentry'),
('collectiveExclusions', 'collectiveExclusions'),
('configContext', 'configContext'),
('contextCSN', 'contextCSN'),
('createTimestamp', 'createTimestamp'),
('creatorsName', 'creatorsName'),
('crossCertificatePair', 'crossCertificatePair'),
('dITContentRules', 'dITContentRules'),
('dITRedirect', 'dITRedirect'),
('dITStructureRules', 'dITStructureRules'),
('dSAQuality', 'dSAQuality'),
('dc', 'dc'),
('deltaRevocationList', 'deltaRevocationList'),
('departmentNumber', 'departmentNumber'),
('description', 'description'),
('destinationIndicator', 'destinationIndicator'),
('displayName', 'displayName'),
('distinguishedName', 'distinguishedName'),
('dmdName', 'dmdName'),
('dnQualifier', 'dnQualifier'),
('documentAuthor', 'documentAuthor'),
('documentIdentifier', 'documentIdentifier'),
('documentLocation', 'documentLocation'),
('documentPublisher', 'documentPublisher'),
('documentTitle', 'documentTitle'),
('documentVersion', 'documentVersion'),
('drink', 'drink'),
('dynamicObject', 'dynamicObject'),
('dynamicSubtrees', 'dynamicSubtrees'),
('eduOrgHomePageURI', 'eduOrgHomePageURI'),
('eduOrgIdentityAuthNPolicyURI', 'eduOrgIdentityAuthNPolicyURI'),
('eduOrgLegalName', 'eduOrgLegalName'),
('eduOrgSuperiorURI', 'eduOrgSuperiorURI'),
('eduOrgWhitePagesURI', 'eduOrgWhitePagesURI'),
('eduPersonAffiliation', 'eduPersonAffiliation'),
('eduPersonAssurance', 'eduPersonAssurance'),
('eduPersonEntitlement', 'eduPersonEntitlement'),
('eduPersonNickname', 'eduPersonNickname'),
('eduPersonOrgDN', 'eduPersonOrgDN'),
('eduPersonOrgUnitDN', 'eduPersonOrgUnitDN'),
('eduPersonPrimaryAffiliation', 'eduPersonPrimaryAffiliation'),
('eduPersonPrimaryOrgUnitDN', 'eduPersonPrimaryOrgUnitDN'),
('eduPersonPrincipalName', 'eduPersonPrincipalName'),
('eduPersonScopedAffiliation', 'eduPersonScopedAffiliation'),
('eduPersonTargetedID', 'eduPersonTargetedID'),
('email', 'email'),
('employeeNumber', 'employeeNumber'),
('employeeType', 'employeeType'),
('enhancedSearchGuide', 'enhancedSearchGuide'),
('entry', 'entry'),
('entryCSN', 'entryCSN'),
('entryDN', 'entryDN'),
('entryTtl', 'entryTtl'),
('entryUUID', 'entryUUID'),
('extensibleObject', 'extensibleObject'),
('fax', 'fax'),
('gecos', 'gecos'),
('generationQualifier', 'generationQualifier'),
('gidNumber', 'gidNumber'),
('givenName', 'givenName'),
('glue', 'glue'),
('hasSubordinates', 'hasSubordinates'),
('homeDirectory', 'homeDirectory'),
('homePhone', 'homePhone'),
('homePostalAddress', 'homePostalAddress'),
('host', 'host'),
('houseIdentifier', 'houseIdentifier'),
('info', 'info'),
('initials', 'initials'),
('internationaliSDNNumber', 'internationaliSDNNumber'),
('ipHostNumber', 'ipHostNumber'),
('ipNetmaskNumber', 'ipNetmaskNumber'),
('ipNetworkNumber', 'ipNetworkNumber'),
('ipProtocolNumber', 'ipProtocolNumber'),
('ipServicePort', 'ipServicePort'),
('ipServiceProtocolSUPname', 'ipServiceProtocolSUPname'),
('janetMailbox', 'janetMailbox'),
('jpegPhoto', 'jpegPhoto'),
('knowledgeInformation', 'knowledgeInformation'),
('l', 'l'),
('labeledURI', 'labeledURI'),
('ldapSyntaxes', 'ldapSyntaxes'),
('loginShell', 'loginShell'),
('mDRecord', 'mDRecord'),
('mXRecord', 'mXRecord'),
('macAddress', 'macAddress'),
('mail', 'mail'),
('mailForwardingAddress', 'mailForwardingAddress'),
('mailHost', 'mailHost'),
('mailLocalAddress', 'mailLocalAddress'),
('mailPreferenceOption', 'mailPreferenceOption'),
('mailRoutingAddress', 'mailRoutingAddress'),
('manager', 'manager'),
('matchingRuleUse', 'matchingRuleUse'),
('matchingRules', 'matchingRules'),
('member', 'member'),
('memberNisNetgroup', 'memberNisNetgroup'),
('memberUid', 'memberUid'),
('mobile', 'mobile'),
('modifiersName', 'modifiersName'),
('modifyTimestamp', 'modifyTimestamp'),
('monitorContext', 'monitorContext'),
('nSRecord', 'nSRecord'),
('name', 'name'),
('nameForms', 'nameForms'),
('namingCSN', 'namingCSN'),
('namingContexts', 'namingContexts'),
('nisMapEntry', 'nisMapEntry'),
('nisMapNameSUPname', 'nisMapNameSUPname'),
('nisNetgroupTriple', 'nisNetgroupTriple'),
('o', 'o'),
('objectClass', 'objectClass'),
('objectClasses', 'objectClasses'),
('oncRpcNumber', 'oncRpcNumber'),
('organizationalStatus', 'organizationalStatus'),
('otherMailbox', 'otherMailbox'),
('ou', 'ou'),
('owner', 'owner'),
('pager', 'pager'),
('personalSignature', 'personalSignature'),
('personalTitle', 'personalTitle'),
('photo', 'photo'),
('physicalDeliveryOfficeName', 'physicalDeliveryOfficeName'),
('postOfficeBox', 'postOfficeBox'),
('postalAddress', 'postalAddress'),
('postalCode', 'postalCode'),
('preferredDeliveryMethod', 'preferredDeliveryMethod'),
('preferredLanguage', 'preferredLanguage'),
('presentationAddress', 'presentationAddress'),
('protocolInformation', 'protocolInformation'),
('pseudonym', 'pseudonym'),
('ref', 'ref'),
('referral', 'referral'),
('registeredAddress', 'registeredAddress'),
('rfc822MailMember', 'rfc822MailMember'),
('role', 'role'),
('roleOccupant', 'roleOccupant'),
('roomNumber', 'roomNumber'),
('sOARecord', 'sOARecord'),
('schacHomeOrganization', 'schacHomeOrganization'),
('schacHomeOrganizationType', 'schacHomeOrganizationType'),
('searchGuide', 'searchGuide'),
('secretary', 'secretary'),
('seeAlso', 'seeAlso'),
('serialNumber', 'serialNumber'),
('shadowExpire', 'shadowExpire'),
('shadowFlag', 'shadowFlag'),
('shadowInactive', 'shadowInactive'),
('shadowLastChange', 'shadowLastChange'),
('shadowMax', 'shadowMax'),
('shadowMin', 'shadowMin'),
('shadowWarning', 'shadowWarning'),
('singleLevelQuality', 'singleLevelQuality'),
('sn', 'sn'),
('st', 'st'),
('street', 'street'),
('structuralObjectClass', 'structuralObjectClass'),
('subentry', 'subentry'),
('subschema', 'subschema'),
('subschemaSubentry', 'subschemaSubentry'),
('subtreeMaximumQuality', 'subtreeMaximumQuality'),
('subtreeMinimumQuality', 'subtreeMinimumQuality'),
('subtreeSpecification', 'subtreeSpecification'),
('supannActivite', 'supannActivite'),
('supannAffectation', 'supannAffectation'),
('supannAliasLogin', 'supannAliasLogin'),
('supannAutreMail', 'supannAutreMail'),
('supannAutreTelephone', 'supannAutreTelephone'),
('supannCivilite', 'supannCivilite'),
('supannCodeEntite', 'supannCodeEntite'),
('supannCodeEntiteParent', 'supannCodeEntiteParent'),
('supannCodeINE', 'supannCodeINE'),
('supannEmpCorps', 'supannEmpCorps'),
('supannEmpId', 'supannEmpId'),
('supannEntiteAffectation', 'supannEntiteAffectation'),
('supannEntiteAffectationPrincipale', 'supannEntiteAffectationPrincipale'),
('supannEtablissement', 'supannEtablissement'),
('supannEtuAnneeInscription', 'supannEtuAnneeInscription'),
('supannEtuCursusAnnee', 'supannEtuCursusAnnee'),
('supannEtuDiplome', 'supannEtuDiplome'),
('supannEtuElementPedagogique', 'supannEtuElementPedagogique'),
('supannEtuEtape', 'supannEtuEtape'),
('supannEtuId', 'supannEtuId'),
('supannEtuInscription', 'supannEtuInscription'),
('supannEtuRegimeInscription', 'supannEtuRegimeInscription'),
('supannEtuSecteurDisciplinaire', 'supannEtuSecteurDisciplinaire'),
('supannEtuTypeDiplome', 'supannEtuTypeDiplome'),
('supannGroupeAdminDN', 'supannGroupeAdminDN'),
('supannGroupeDateFin', 'supannGroupeDateFin'),
('supannGroupeLecteurDN', 'supannGroupeLecteurDN'),
('supannListeRouge', 'supannListeRouge'),
('supannMailPerso', 'supannMailPerso'),
('supannOrganisme', 'supannOrganisme'),
('supannParrainDN', 'supannParrainDN'),
('supannRefId', 'supannRefId'),
('supannRole', 'supannRole'),
('supannRoleEntite', 'supannRoleEntite'),
('supannRoleGenerique', 'supannRoleGenerique'),
('supannTypeEntite', 'supannTypeEntite'),
('supannTypeEntiteAffectation', 'supannTypeEntiteAffectation'),
('superiorUUID', 'superiorUUID'),
('supportedAlgorithms', 'supportedAlgorithms'),
('supportedApplicationContext', 'supportedApplicationContext'),
('supportedAuthPasswordSchemes', 'supportedAuthPasswordSchemes'),
('supportedControl', 'supportedControl'),
('supportedExtension', 'supportedExtension'),
('supportedFeatures', 'supportedFeatures'),
('supportedLDAPVersion', 'supportedLDAPVersion'),
('supportedSASLMechanisms', 'supportedSASLMechanisms'),
('syncConsumerSubentry', 'syncConsumerSubentry'),
('syncProviderSubentry', 'syncProviderSubentry'),
('syncTimestamp', 'syncTimestamp'),
('syncreplCookie', 'syncreplCookie'),
('telephoneNumber', 'telephoneNumber'),
('teletexTerminalIdentifier', 'teletexTerminalIdentifier'),
('telexNumber', 'telexNumber'),
('textEncodedORAddress', 'textEncodedORAddress'),
('title', 'title'),
('top', 'top'),
('uid', 'uid'),
('uidNumber', 'uidNumber'),
('uniqueIdentifier', 'uniqueIdentifier'),
('uniqueMember', 'uniqueMember'),
('userCertificate', 'userCertificate'),
('userClass', 'userClass'),
('userPKCS12', 'userPKCS12'),
('userPassword', 'userPassword'),
('userSMIMECertificate', 'userSMIMECertificate'),
('vendorName', 'vendorName'),
('vendorVersion', 'vendorVersion'),
('x121Address', 'x121Address'),
('x500UniqueIdentifier', 'x500UniqueIdentifier'),
],
),
),
(
'output_name_format',
models.CharField(
default=('urn:oasis:names:tc:SAML:2.0:attrname-format:uri', 'SAMLv2 URI'),
max_length=100,
verbose_name='Output name format',
choices=[
('urn:oasis:names:tc:SAML:2.0:attrname-format:uri', 'SAMLv2 URI'),
('urn:oasis:names:tc:SAML:2.0:attrname-format:basic', 'SAMLv2 BASIC'),
],
),
),
(
'output_namespace',
models.CharField(
default=('Default', 'Default'),
max_length=100,
verbose_name='Output namespace',
choices=[
('Default', 'Default'),
(
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims',
),
],
),
),
('required', models.BooleanField(default=False, verbose_name='Required')),
],
options={
'verbose_name': 'attribute list item',
'verbose_name_plural': 'attribute list items',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='AttributeList',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(unique=True, max_length=100, verbose_name='Name')),
(
'attributes',
models.ManyToManyField(
related_name='attributes of the list',
null=True,
verbose_name='Attributes',
to='attribute_aggregator.AttributeItem',
blank=True,
),
),
],
options={
'verbose_name': 'attribute list',
'verbose_name_plural': 'attribute lists',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='AttributeSource',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(unique=True, max_length=200, verbose_name='Name')),
(
'namespace',
models.CharField(
default=('Default', 'Default'),
max_length=100,
verbose_name='Namespace',
choices=[
('Default', 'Default'),
(
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims',
),
],
),
),
],
options={
'verbose_name': 'attribute source',
'verbose_name_plural': 'attribute sources',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='LdapSource',
fields=[
(
'attributesource_ptr',
models.OneToOneField(
parent_link=True,
auto_created=True,
primary_key=True,
serialize=False,
to='attribute_aggregator.AttributeSource',
on_delete=models.CASCADE,
),
),
('server', models.CharField(unique=True, max_length=200, verbose_name='Server')),
('user', models.CharField(max_length=200, null=True, verbose_name='User', blank=True)),
(
'password',
models.CharField(max_length=200, null=True, verbose_name='Password', blank=True),
),
('base', models.CharField(max_length=200, verbose_name='Base')),
('port', models.IntegerField(default=389, verbose_name='Port')),
('ldaps', models.BooleanField(default=False, verbose_name='LDAPS')),
('certificate', models.TextField(verbose_name='Certificate', blank=True)),
(
'is_auth_backend',
models.BooleanField(default=False, verbose_name='Is it used for authentication?'),
),
],
options={
'verbose_name': 'ldap attribute source',
'verbose_name_plural': 'ldap attribute sources',
},
bases=('attribute_aggregator.attributesource',),
),
migrations.CreateModel(
name='UserAliasInSource',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(max_length=200, verbose_name='Name')),
(
'source',
models.ForeignKey(
verbose_name='attribute source',
to='attribute_aggregator.AttributeSource',
on_delete=models.CASCADE,
),
),
(
'user',
models.ForeignKey(
related_name='user_alias_in_source',
verbose_name='user',
to='auth.User',
on_delete=models.CASCADE,
),
),
],
options={
'verbose_name': 'user alias from source',
'verbose_name_plural': 'user aliases from source',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='UserAttributeProfile',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('data', models.TextField(null=True, blank=True)),
(
'user',
models.OneToOneField(
related_name='user_attribute_profile',
null=True,
blank=True,
to='auth.User',
on_delete=models.CASCADE,
),
),
],
options={
'verbose_name': 'user attribute profile',
'verbose_name_plural': 'user attribute profiles',
},
bases=(models.Model,),
),
migrations.AlterUniqueTogether(
name='useraliasinsource',
unique_together={('name', 'source')},
),
migrations.AddField(
model_name='attributeitem',
name='source',
field=models.ForeignKey(
verbose_name='Attribute source',
blank=True,
to='attribute_aggregator.AttributeSource',
null=True,
on_delete=models.CASCADE,
),
preserve_default=True,
),
]

View File

@ -1,35 +0,0 @@
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('attribute_aggregator', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='useraliasinsource',
name='user',
field=models.ForeignKey(
related_name='user_alias_in_source',
verbose_name='user',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
),
preserve_default=True,
),
migrations.AlterField(
model_name='userattributeprofile',
name='user',
field=models.OneToOneField(
related_name='user_attribute_profile',
null=True,
blank=True,
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
),
preserve_default=True,
),
]

View File

@ -1,25 +0,0 @@
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('attribute_aggregator', '0002_auto_20150409_1840'),
]
operations = [
migrations.AlterField(
model_name='attributelist',
name='attributes',
field=models.ManyToManyField(
to='attribute_aggregator.AttributeItem', verbose_name='Attributes', blank=True
),
),
migrations.AlterField(
model_name='useraliasinsource',
name='user',
field=models.ForeignKey(
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
),
),
]

View File

@ -1,57 +0,0 @@
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('idp', '0003_auto_20150915_2041'),
('attribute_aggregator', '0003_auto_20150526_2239'),
]
operations = [
migrations.RemoveField(
model_name='attributeitem',
name='source',
),
migrations.RemoveField(
model_name='attributelist',
name='attributes',
),
migrations.DeleteModel(
name='AttributeItem',
),
migrations.DeleteModel(
name='AttributeList',
),
migrations.RemoveField(
model_name='ldapsource',
name='attributesource_ptr',
),
migrations.AlterUniqueTogether(
name='useraliasinsource',
unique_together=None,
),
migrations.RemoveField(
model_name='useraliasinsource',
name='source',
),
migrations.DeleteModel(
name='LdapSource',
),
migrations.DeleteModel(
name='AttributeSource',
),
migrations.RemoveField(
model_name='useraliasinsource',
name='user',
),
migrations.DeleteModel(
name='UserAliasInSource',
),
migrations.RemoveField(
model_name='userattributeprofile',
name='user',
),
migrations.DeleteModel(
name='UserAttributeProfile',
),
]

View File

@ -14,10 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
from authentic2.a2_rbac.models import Role
from authentic2.custom_user.models import User
from ...decorators import to_list
from ...models import Attribute, AttributeValue
@ -33,7 +33,6 @@ def get_instances(ctx):
@to_list
def get_attribute_names(instance, ctx):
User = get_user_model()
for field in User._meta.fields:
if field.name == 'ou':
continue
@ -69,7 +68,6 @@ def get_dependencies(instance, ctx):
def get_attributes(instance, ctx):
user = ctx.get('user')
User = get_user_model()
if not user or not isinstance(user, User):
return ctx
for field in User._meta.fields:

View File

@ -1,135 +0,0 @@
from django.core import validators
from django.db import migrations, models
from django.utils import timezone
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '__first__'),
]
operations = [
migrations.CreateModel(
name='Permission',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(max_length=50, verbose_name='name')),
(
'content_type',
models.ForeignKey(to='contenttypes.ContentType', to_field='id', on_delete=models.CASCADE),
),
('codename', models.CharField(max_length=100, verbose_name='codename')),
],
options={
'ordering': ('content_type__app_label', 'content_type__model', 'codename'),
'verbose_name': 'permission',
'verbose_name_plural': 'permissions',
},
),
migrations.CreateModel(
name='Group',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(unique=True, max_length=80, verbose_name='name')),
(
'permissions',
models.ManyToManyField(to='auth.Permission', verbose_name='permissions', blank=True),
),
],
options={
'verbose_name': 'group',
'verbose_name_plural': 'groups',
},
),
migrations.CreateModel(
name='User',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(default=timezone.now, verbose_name='last login')),
(
'is_superuser',
models.BooleanField(
default=False,
help_text=(
'Designates that this user has all permissions without explicitly assigning them.'
),
verbose_name='superuser status',
),
),
(
'username',
models.CharField(
help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.',
unique=True,
max_length=30,
verbose_name='username',
validators=[
validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username.', 'invalid')
],
),
),
('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)),
('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)),
('email', models.EmailField(max_length=75, verbose_name='email address', blank=True)),
(
'is_staff',
models.BooleanField(
default=False,
help_text='Designates whether the user can log into this admin site.',
verbose_name='staff status',
),
),
(
'is_active',
models.BooleanField(
default=True,
help_text=(
'Designates whether this user should be treated as active. Unselect this instead'
' of deleting accounts.'
),
verbose_name='active',
),
),
('date_joined', models.DateTimeField(default=timezone.now, verbose_name='date joined')),
(
'groups',
models.ManyToManyField(
to='auth.Group',
verbose_name='groups',
blank=True,
help_text=(
'The groups this user belongs to. A user will get all permissions granted to each'
' of his/her group.'
),
related_name='+',
related_query_name='+',
),
),
(
'user_permissions',
models.ManyToManyField(
to='auth.Permission',
verbose_name='user permissions',
blank=True,
help_text='Specific permissions for this user.',
related_name='+',
related_query_name='+',
),
),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
},
),
]

View File

@ -1,30 +0,0 @@
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(
help_text=(
'Required, 255 characters or fewer. Only letters, numbers, and @, ., +, -, or _'
' characters.'
),
unique=True,
max_length=255,
verbose_name='username',
validators=[
django.core.validators.RegexValidator(
'^[\\w.@+-]+$', 'Enter a valid username.', 'invalid'
)
],
),
preserve_default=True,
),
]

View File

@ -1,13 +0,0 @@
from django.conf import settings
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('auth', '0002_auto_20150323_1720'),
]
operations = [
migrations.DeleteModel('User'),
]

View File

@ -1,111 +0,0 @@
import django.core.validators
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0003_auto_20150410_1657'),
('authentic2', '__first__'),
('authentic2_idp_cas', '__first__'),
('saml', '__first__'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('password', models.CharField(max_length=128, verbose_name='password')),
(
'last_login',
models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login'),
),
(
'is_superuser',
models.BooleanField(
default=False,
help_text=(
'Designates that this user has all permissions without explicitly assigning them.'
),
verbose_name='superuser status',
),
),
(
'username',
models.CharField(
help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.',
unique=True,
max_length=30,
verbose_name='username',
validators=[
django.core.validators.RegexValidator(
'^[\\w.@+-]+$', 'Enter a valid username.', 'invalid'
)
],
),
),
('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)),
('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)),
('email', models.EmailField(max_length=75, verbose_name='email address', blank=True)),
(
'is_staff',
models.BooleanField(
default=False,
help_text='Designates whether the user can log into this admin site.',
verbose_name='staff status',
),
),
(
'is_active',
models.BooleanField(
default=True,
help_text=(
'Designates whether this user should be treated as active. Unselect this instead'
' of deleting accounts.'
),
verbose_name='active',
),
),
(
'date_joined',
models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined'),
),
(
'groups',
models.ManyToManyField(
related_query_name='user',
related_name='user_set',
to='auth.Group',
blank=True,
help_text=(
'The groups this user belongs to. A user will get all permissions granted to each'
' of his/her group.'
),
verbose_name='groups',
),
),
(
'user_permissions',
models.ManyToManyField(
related_query_name='user',
related_name='user_set',
to='auth.Permission',
blank=True,
help_text='Specific permissions for this user.',
verbose_name='user permissions',
),
),
],
options={
'abstract': False,
'verbose_name': 'user',
'swappable': 'AUTH_USER_MODEL',
'verbose_name_plural': 'users',
},
bases=(models.Model,),
),
]

View File

@ -1,79 +0,0 @@
import django.contrib.auth.models
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0004_user'),
]
operations = [
migrations.AlterModelManagers(
name='group',
managers=[
('objects', django.contrib.auth.models.GroupManager()),
],
),
migrations.AlterModelManagers(
name='permission',
managers=[
('objects', django.contrib.auth.models.PermissionManager()),
],
),
migrations.AlterModelManagers(
name='user',
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.AlterField(
model_name='permission',
name='name',
field=models.CharField(max_length=255, verbose_name='name'),
),
migrations.AlterField(
model_name='user',
name='email',
field=models.EmailField(max_length=254, verbose_name='email address', blank=True),
),
migrations.AlterField(
model_name='user',
name='groups',
field=models.ManyToManyField(
related_query_name='user',
related_name='user_set',
to='auth.Group',
blank=True,
help_text=(
'The groups this user belongs to. A user will get all permissions granted to each of'
' their groups.'
),
verbose_name='groups',
),
),
migrations.AlterField(
model_name='user',
name='last_login',
field=models.DateTimeField(null=True, verbose_name='last login', blank=True),
),
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(
error_messages={'unique': 'A user with that username already exists.'},
max_length=30,
validators=[
django.core.validators.RegexValidator(
'^[\\w.@+-]+$',
'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_'
' characters.',
'invalid',
)
],
help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.',
unique=True,
verbose_name='username',
),
),
]

View File

@ -14,13 +14,11 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib.auth import get_user_model
from authentic2 import app_settings
def get_user_queryset():
User = get_user_model()
from authentic2.custom_user.models import User
qs = User.objects.all()

View File

@ -33,7 +33,6 @@ import ldap.modlist
import ldap.sasl
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.core.cache import cache
from django.core.exceptions import ImproperlyConfigured
@ -52,6 +51,7 @@ from authentic2.a2_rbac.models import OrganizationalUnit, Role
from authentic2.a2_rbac.utils import get_default_ou
from authentic2.backends import is_user_authenticable
from authentic2.compat_lasso import lasso
from authentic2.custom_user.models import User
from authentic2.ldap_utils import FilterFormatter
from authentic2.middleware import StoreRequestMiddleware
from authentic2.models import Lock, UserExternalId
@ -65,8 +65,6 @@ from authentic2.utils.misc import PasswordChangeError, get_password_authenticato
log = logging.getLogger(__name__)
User = get_user_model()
DEFAULT_CA_BUNDLE = ''
CA_BUNDLE_PATHS = [
@ -393,24 +391,24 @@ class LDAPUser(User):
log.warning('ldap: check_password failed, could not get a connection')
return False
def set_password(self, new_password):
def set_password(self, raw_password):
# Allow change password to work in all cases, as the form does a check_password() first
# if the verify pass, we have the old password stored in self._current_password
_current_password = getattr(self, '_current_password', None) or self.get_password_in_session()
if _current_password != new_password:
if _current_password != raw_password:
conn = self.get_connection()
if not conn:
log.warning('ldap: set_password failed, could not get a connection')
return
try:
self.ldap_backend.modify_password(conn, self.block, self.dn, _current_password, new_password)
self.ldap_backend.modify_password(conn, self.block, self.dn, _current_password, raw_password)
except ldap.LDAPError as e:
log.warning('ldap: set_password failed (%s)', ldap_error_str(e))
raise PasswordChangeError(_('LDAP directory refused the password change.'))
self._current_password = new_password
self.keep_password_in_session(new_password)
self._current_password = raw_password
self.keep_password_in_session(raw_password)
if self.block['keep_password']:
super().set_password(new_password)
super().set_password(raw_password)
else:
self.set_unusable_password()

View File

@ -17,13 +17,13 @@
import functools
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend as BaseModelBackend
from django.contrib.contenttypes.models import ContentType
from django.db import models
from phonenumbers import PhoneNumberFormat, format_number, is_valid_number
from authentic2.backends import get_user_queryset
from authentic2.custom_user.models import User
from authentic2.models import AttributeValue
from authentic2.user_login_failure import user_login_failure, user_login_success
from authentic2.utils.misc import get_password_authenticator, parse_phone_number
@ -40,15 +40,11 @@ PROXY_USER_MODEL = None
class ModelBackend(BaseModelBackend):
"""
Authenticates against settings.AUTH_USER_MODEL.
"""
def get_query(self, username, realm=None, ou=None):
username_field = 'username'
queries = []
password_authenticator = get_password_authenticator()
user_ct = ContentType.objects.get_for_model(get_user_model())
user_ct = ContentType.objects.get_for_model(User)
if password_authenticator.accept_email_authentication:
queries.append(models.Q(**{'email__iexact': username}))
if password_authenticator.is_phone_authn_active:
@ -82,14 +78,13 @@ class ModelBackend(BaseModelBackend):
return bool(a2_models.PasswordReset.filter(user=user).count())
def authenticate(self, request, username=None, password=None, realm=None, ou=None):
UserModel = get_user_model()
if not username:
return
query = self.get_query(username=username, realm=realm, ou=ou)
users = get_user_queryset().filter(query)
# order by username to make username without realm come before usernames with realms
# i.e. "toto" should come before "toto@example.com"
users = users.order_by('-is_active', UserModel.USERNAME_FIELD, 'id')
users = users.order_by('-is_active', User.USERNAME_FIELD, 'id')
for user in users:
if user.check_password(password):
user_login_success(user.get_username())
@ -100,10 +95,9 @@ class ModelBackend(BaseModelBackend):
request.failed_logins.update({user: {}})
def get_user(self, user_id):
UserModel = get_user_model()
try:
user = UserModel._default_manager.get(pk=user_id)
except UserModel.DoesNotExist:
user = User._default_manager.get(pk=user_id)
except User.DoesNotExist:
return None
return user

View File

@ -38,12 +38,12 @@ class CustomUserConfig(AppConfig):
self, app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs
):
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.utils import translation
from django.utils.translation import gettext_lazy as _
from authentic2.attribute_kinds import get_kind
from authentic2.custom_user.models import User
from authentic2.models import Attribute, AttributeValue
if not router.allow_migrate(using, Attribute):
@ -53,7 +53,6 @@ class CustomUserConfig(AppConfig):
return
translation.activate(settings.LANGUAGE_CODE)
User = get_user_model()
content_type = ContentType.objects.get_for_model(User)
attrs = {}

View File

@ -17,13 +17,14 @@
import getpass
from django.contrib.auth import get_user_model
from django.core.exceptions import MultipleObjectsReturned
from django.core.management.base import BaseCommand, CommandError
from django.db import DEFAULT_DB_ALIAS
from django.db.models.query import Q
from django.utils.encoding import force_str
from authentic2.custom_user.models import User
class Command(BaseCommand):
help = "Change a user's password for django.contrib.auth."
@ -51,13 +52,11 @@ class Command(BaseCommand):
if not username:
username = getpass.getuser()
UserModel = get_user_model()
qs = UserModel._default_manager.using(options.get('database'))
qs = User._default_manager.using(options.get('database'))
qs = qs.filter(Q(uuid=username) | Q(username=username) | Q(email__iexact=username))
try:
u = qs.get()
except UserModel.DoesNotExist:
except User.DoesNotExist:
raise CommandError("user '%s' does not exist" % username)
except MultipleObjectsReturned:
while True:

View File

@ -5,80 +5,6 @@ import authentic2.utils.misc
import authentic2.validators
def noop(apps, schema_editor):
pass
def copy_old_users_to_custom_user_model(apps, schema_editor):
OldUser = apps.get_model('auth', 'User')
NewUser = apps.get_model('custom_user', 'User')
fields = [
'id',
'username',
'email',
'first_name',
'last_name',
'is_staff',
'is_active',
'date_joined',
'is_superuser',
'last_login',
'password',
]
old_users = OldUser.objects.prefetch_related('groups', 'user_permissions').order_by('id')
new_users = []
for old_user in old_users:
new_user = NewUser()
for field in fields:
setattr(new_user, field, getattr(old_user, field))
new_users.append(new_user)
# mass create of new users
NewUser.objects.bulk_create(new_users)
new_groups = []
new_permissions = []
GroupThrough = NewUser.groups.through
PermissionThrough = NewUser.user_permissions.through
new_users = NewUser.objects.order_by('id')
for old_user, new_user in zip(old_users, new_users):
assert old_user.id == new_user.id
for group in old_user.groups.all():
new_groups.append(GroupThrough(user_id=new_user.id, group_id=group.id))
for dummy in old_user.user_permissions.all():
new_permissions.append(PermissionThrough(user_id=new_user.id, group_id=group.id))
# mass create group and permission relations
GroupThrough.objects.bulk_create(new_groups)
PermissionThrough.objects.bulk_create(new_permissions)
# Reset sequences
if schema_editor.connection.vendor == 'postgresql':
schema_editor.execute(
'SELECT setval(pg_get_serial_sequence(\'"custom_user_user_groups"\',\'id\'), coalesce(max("id"),'
' 1), max("id") IS NOT null) FROM "custom_user_user_groups";'
)
schema_editor.execute(
'SELECT setval(pg_get_serial_sequence(\'"custom_user_user_user_permissions"\',\'id\'),'
' coalesce(max("id"), 1), max("id") IS NOT null) FROM "custom_user_user_user_permissions";'
)
schema_editor.execute(
'SELECT setval(pg_get_serial_sequence(\'"custom_user_user"\',\'id\'), coalesce(max("id"), 1),'
' max("id") IS NOT null) FROM "custom_user_user";'
)
elif schema_editor.connection.vendor == 'sqlite':
schema_editor.execute(
'UPDATE sqlite_sequence SET seq = (SELECT MAX(id) FROM custom_user_user) WHERE name ='
' "custom_user_user";'
)
schema_editor.execute(
'UPDATE sqlite_sequence SET seq = (SELECT MAX(id) FROM custom_user_user_groups) WHERE name ='
' "custom_user_user_groups";'
)
schema_editor.execute(
'UPDATE sqlite_sequence SET seq = (SELECT MAX(id) FROM custom_user_user_user_permissions) WHERE'
' name = "custom_user_user_permissions";'
)
else:
raise NotImplementedError()
class Migration(migrations.Migration):
dependencies = [
('auth', '__first__'),
@ -187,5 +113,4 @@ class Migration(migrations.Migration):
},
bases=(models.Model,),
),
migrations.RunPython(copy_old_users_to_custom_user_model, reverse_code=noop),
]

View File

@ -1,4 +1,3 @@
from django.conf import settings
from django.db import migrations, models
@ -37,11 +36,6 @@ class ThirdPartyAlterField(migrations.AlterField):
class Migration(migrations.Migration):
dependencies = [
('custom_user', '0001_initial'),
('admin', '__first__'),
]
run_before = [
('auth', '0003_auto_20150410_1657'),
]
operations = [
@ -50,7 +44,7 @@ class Migration(migrations.Migration):
app_label='admin',
model_name='logentry',
name='user',
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
field=models.ForeignKey(to='custom_user.User', on_delete=models.CASCADE),
preserve_default=True,
),
]

View File

@ -9,7 +9,6 @@ class Migration(migrations.Migration):
dependencies = [
('contenttypes', '__first__'),
('custom_user', '0010_auto_20160307_1418'),
('authentic2', '0015_auto_20160621_1711'),
]
operations = [

View File

@ -4,7 +4,6 @@ import uuid
import django
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
from django.db.models import JSONField
@ -64,7 +63,7 @@ class Migration(migrations.Migration):
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='profiles',
to=settings.AUTH_USER_MODEL,
to='custom_user.User',
verbose_name='user',
),
),

View File

@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentic2', '0035_service_home_url'),
('custom_user', '0029_profile_profiletype'),
]

View File

@ -0,0 +1,358 @@
# Generated by Django 3.2.23 on 2023-12-14 21:01
import uuid
import django.contrib.postgres.fields
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models
import authentic2.utils.misc
import authentic2.validators
class Migration(migrations.Migration):
replaces = [
('custom_user', '0001_initial'),
('custom_user', '0002_auto_20150410_1823'),
('custom_user', '0003_auto_20150504_1410'),
('custom_user', '0004_user_ou'),
('custom_user', '0005_auto_20150522_1527'),
('custom_user', '0006_auto_20150527_1212'),
('custom_user', '0007_auto_20150610_1527'),
('custom_user', '0008_auto_20150617_1606'),
('custom_user', '0009_auto_20150810_1953'),
('custom_user', '0010_auto_20160307_1418'),
('custom_user', '0011_manual_attribute_values_for_name_fields'),
('custom_user', '0012_user_modified'),
('custom_user', '0013_user_email_verified'),
('custom_user', '0014_set_email_verified'),
('custom_user', '0015_auto_20170707_1653'),
('custom_user', '0016_auto_20180925_1107'),
('custom_user', '0017_auto_20200305_1645'),
('custom_user', '0018_user_last_account_deletion_alert'),
('custom_user', '0019_add_user_deleted'),
('custom_user', '0020_deleteduser'),
('custom_user', '0021_set_unusable_password'),
('custom_user', '0022_index_email'),
('custom_user', '0023_index_username'),
('custom_user', '0024_index_email_by_trigrams'),
('custom_user', '0025_user_deactivation'),
('custom_user', '0026_remove_user_deleted'),
('custom_user', '0027_user_deactivation_reason'),
('custom_user', '0028_user_email_verified_date'),
('custom_user', '0029_profile_profiletype'),
('custom_user', '0030_auto_20220304_1136'),
('custom_user', '0031_profile_email'),
('custom_user', '0032_auto_20220919_1230'),
('custom_user', '0032_index_deleteduser_old_email'),
('custom_user', '0032_index_deleteduser_old_uuid'),
('custom_user', '0033_user_keepalive'),
('custom_user', '0034_user_email_verified_sources'),
('custom_user', '0035_alter_user_username'),
('custom_user', '0036_remove_user_constraint_at_least_one_identifier'),
]
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
('a2_rbac', '0040_replace'),
('authentic2', '0051_replace'),
]
operations = [
migrations.CreateModel(
name='DeletedUser',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('deleted', models.DateTimeField(auto_now_add=True, verbose_name='Deletion date')),
('old_uuid', models.TextField(blank=True, db_index=True, null=True, verbose_name='Old UUID')),
(
'old_user_id',
models.PositiveIntegerField(blank=True, null=True, verbose_name='Old user id'),
),
(
'old_email',
models.EmailField(
blank=True, db_index=True, max_length=254, null=True, verbose_name='Old email adress'
),
),
(
'old_phone',
models.CharField(blank=True, max_length=64, null=True, verbose_name='Old phone number'),
),
('old_data', models.JSONField(blank=True, null=True, verbose_name='Old data')),
],
options={
'verbose_name': 'deleted user',
'verbose_name_plural': 'deleted users',
'ordering': ('deleted', 'id'),
},
),
migrations.CreateModel(
name='ProfileType',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
(
'uuid',
models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name='UUID'),
),
('name', models.CharField(max_length=64, verbose_name='name')),
('slug', models.SlugField(max_length=64, unique=True, verbose_name='slug')),
],
options={
'verbose_name': 'profile type',
'verbose_name_plural': 'profile types',
'ordering': ('name', 'slug'),
},
),
migrations.CreateModel(
name='User',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
(
'uuid',
models.CharField(
default=authentic2.utils.misc.get_hex_uuid,
editable=False,
max_length=32,
unique=True,
verbose_name='uuid',
),
),
(
'username',
models.CharField(
blank=True, db_index=True, max_length=256, null=True, verbose_name='username'
),
),
('first_name', models.CharField(blank=True, max_length=128, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=128, verbose_name='last name')),
(
'email',
models.EmailField(
blank=True,
max_length=254,
validators=[authentic2.validators.EmailValidator()],
verbose_name='email address',
),
),
('email_verified', models.BooleanField(default=False, verbose_name='email verified')),
(
'email_verified_date',
models.DateTimeField(
blank=True, default=None, null=True, verbose_name='email verified date'
),
),
(
'email_verified_sources',
django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=63),
blank=True,
default=list,
null=True,
size=None,
verbose_name='email verification sources',
),
),
(
'is_superuser',
models.BooleanField(
default=False,
help_text='Designates that this user has all permissions without explicitly assigning them.',
verbose_name='superuser status',
),
),
(
'phone',
models.CharField(
blank=True,
max_length=64,
null=True,
validators=[authentic2.validators.PhoneNumberValidator],
verbose_name='phone number',
),
),
(
'phone_verified_on',
models.DateTimeField(
blank=True, default=None, null=True, verbose_name='phone verification date'
),
),
(
'is_staff',
models.BooleanField(
default=False,
help_text='Designates whether the user can log into this admin site.',
verbose_name='staff status',
),
),
(
'is_active',
models.BooleanField(
default=True,
help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.',
verbose_name='active',
),
),
(
'date_joined',
models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined'),
),
(
'modified',
models.DateTimeField(auto_now=True, db_index=True, verbose_name='Last modification time'),
),
(
'last_account_deletion_alert',
models.DateTimeField(blank=True, null=True, verbose_name='Last account deletion alert'),
),
(
'deactivation',
models.DateTimeField(blank=True, null=True, verbose_name='Deactivation datetime'),
),
(
'deactivation_reason',
models.TextField(blank=True, null=True, verbose_name='Deactivation reason'),
),
(
'keepalive',
models.DateTimeField(blank=True, null=True, verbose_name='Keepalive timestamp'),
),
(
'groups',
models.ManyToManyField(
blank=True,
help_text='The groups this user belongs to. A user will get all permissions granted to each of his/her group.',
related_name='user_set',
related_query_name='user',
to='auth.Group',
verbose_name='groups',
),
),
(
'ou',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to='a2_rbac.organizationalunit',
verbose_name='organizational unit',
),
),
(
'user_permissions',
models.ManyToManyField(
blank=True,
help_text='Specific permissions for this user.',
related_name='user_set',
related_query_name='user',
to='auth.Permission',
verbose_name='user permissions',
),
),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'ordering': ('last_name', 'first_name', 'email', 'username'),
},
),
migrations.CreateModel(
name='ServiceProfileType',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
(
'profile_type',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to='custom_user.profiletype'
),
),
(
'service',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='authentic2.service'),
),
],
options={
'unique_together': {('service', 'profile_type')},
},
),
migrations.CreateModel(
name='Profile',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('identifier', models.CharField(default='', max_length=256, verbose_name='identifier')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('data', models.JSONField(blank=True, null=True, verbose_name='data')),
(
'profile_type',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='profiles',
to='custom_user.profiletype',
verbose_name='profile type',
),
),
(
'user',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='profiles',
to='custom_user.user',
verbose_name='user',
),
),
],
options={
'verbose_name': 'profile',
'verbose_name_plural': 'profiles',
'ordering': ('user', 'profile_type'),
'unique_together': {('user', 'profile_type', 'identifier')},
},
),
migrations.AddField(
model_name='profiletype',
name='services',
field=models.ManyToManyField(
blank=True,
related_name='_custom_user_profiletype_services_+',
through='custom_user.ServiceProfileType',
to='authentic2.Service',
verbose_name='allowed services for this profile type',
),
),
migrations.RunSQL(
sql=[
'CREATE EXTENSION IF NOT EXISTS unaccent SCHEMA public',
'CREATE EXTENSION IF NOT EXISTS pg_trgm SCHEMA public',
'CREATE OR REPLACE FUNCTION public.immutable_unaccent(text) RETURNS varchar AS $$ SELECT'
" public.unaccent('public.unaccent',$1::text); $$ LANGUAGE 'sql' IMMUTABLE",
'CREATE INDEX custom_user_name_gist_idx ON custom_user_user USING gist'
" (LOWER(public.immutable_unaccent(first_name || ' ' || last_name)) public.gist_trgm_ops)",
],
reverse_sql=[
'DROP INDEX IF EXISTS custom_user_name_gist_idx',
'DROP FUNCTION IF EXISTS public.immutable_unaccent(text)',
'DROP EXTENSION IF EXISTS pg_trgm',
'DROP EXTENSION IF EXISTS unaccent',
],
),
]

View File

@ -19,13 +19,13 @@ from collections import OrderedDict
from django import forms
from django.contrib.auth import forms as auth_forms
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.forms import Form
from django.utils.translation import gettext_lazy as _
from authentic2.backends.ldap_backend import LDAPUser
from authentic2.custom_user.models import User
from authentic2.journal import journal
from authentic2.passwords import validate_password
from authentic2.utils.misc import get_password_authenticator
@ -92,7 +92,7 @@ class PasswordResetForm(HoneypotForm):
def clean_phone(self):
phone = self.cleaned_data.get('phone')
user_ct = ContentType.objects.get_for_model(get_user_model())
user_ct = ContentType.objects.get_for_model(User)
if phone:
user_ids = models.AttributeValue.objects.filter(
attribute=self.authenticator.phone_identifier_field,

View File

@ -17,7 +17,6 @@
import re
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import BaseUserManager
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
@ -26,6 +25,7 @@ from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from authentic2.a2_rbac.models import OrganizationalUnit
from authentic2.custom_user.models import User
from authentic2.forms.fields import CharField, CheckPasswordField, NewPasswordField
from authentic2.passwords import validate_password
@ -36,8 +36,6 @@ from .fields import PhoneField, ValidatedEmailField
from .honeypot import HoneypotForm
from .utils import NextUrlFormMixin
User = get_user_model()
class RegistrationForm(HoneypotForm):
error_css_class = 'form-field-error'

View File

@ -31,7 +31,6 @@ import uuid
import phonenumbers
from django import forms
from django.conf import settings
from django.contrib.auth import get_user_model
from django.forms.widgets import ClearableFileInput, DateInput, DateTimeInput
from django.forms.widgets import EmailInput as BaseEmailInput
from django.forms.widgets import MultiWidget
@ -377,9 +376,11 @@ class SelectAttributeWidget(forms.Select):
@staticmethod
def get_options():
from authentic2.custom_user.models import User
choices = {}
for name in ('email', 'username', 'first_name', 'last_name'):
field = get_user_model()._meta.get_field(name)
field = User._meta.get_field(name)
choices[name] = '%s (%s)' % (capfirst(field.verbose_name), name)
for attribute in Attribute.objects.exclude(name__in=choices):
choices[attribute.name] = '%s (%s)' % (attribute.label, attribute.name)

View File

@ -1,45 +0,0 @@
# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.shortcuts import render
@login_required
def consent_federation(request, nonce='', provider_id=None):
"""On a GET produce a form asking for consentment,
On a POST handle the form and redirect to next"""
if request.method == 'GET':
return render(
request,
'interaction/consent_federation.html',
{
'provider_id': request.GET.get('provider_id', ''),
'nonce': request.GET.get('nonce', ''),
'next': request.GET.get('next', ''),
},
)
else:
next_url = '/'
if 'next' in request.POST:
next_url = request.POST['next']
if 'accept' in request.POST:
next_url = next_url + '&consent_answer=accepted'
return HttpResponseRedirect(next_url)
else:
next_url = next_url + '&consent_answer=refused'
return HttpResponseRedirect(next)

View File

@ -1,122 +0,0 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('attribute_aggregator', '__first__'),
]
operations = [
migrations.CreateModel(
name='AttributePolicy',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(unique=True, max_length=100)),
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
(
'ask_consent_attributes',
models.BooleanField(
default=True, verbose_name='Ask the user consent before forwarding attributes'
),
),
(
'allow_attributes_selection',
models.BooleanField(
default=True, verbose_name='Allow the user to select the forwarding attributes'
),
),
(
'forward_attributes_from_push_sources',
models.BooleanField(default=False, verbose_name='Forward pushed attributes'),
),
(
'map_attributes_from_push_sources',
models.BooleanField(default=False, verbose_name='Map forwarded pushed attributes'),
),
(
'output_name_format',
models.CharField(
default=('urn:oasis:names:tc:SAML:2.0:attrname-format:uri', 'SAMLv2 URI'),
max_length=100,
verbose_name='Output name format',
choices=[
('urn:oasis:names:tc:SAML:2.0:attrname-format:uri', 'SAMLv2 URI'),
('urn:oasis:names:tc:SAML:2.0:attrname-format:basic', 'SAMLv2 BASIC'),
],
),
),
(
'output_namespace',
models.CharField(
default=('Default', 'Default'),
max_length=100,
verbose_name='Output namespace',
choices=[
('Default', 'Default'),
(
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims',
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims',
),
],
),
),
(
'filter_source_of_filtered_attributes',
models.BooleanField(
default=False,
verbose_name='Filter by source and per attribute the forwarded pushed attributes',
),
),
(
'map_attributes_of_filtered_attributes',
models.BooleanField(default=False, verbose_name='Map filtered attributes'),
),
(
'send_error_and_no_attrs_if_missing_required_attrs',
models.BooleanField(
default=False, verbose_name='Send an error when a required attribute is missing'
),
),
(
'attribute_filter_for_sso_from_push_sources',
models.ForeignKey(
related_name='filter attributes of push sources with list',
verbose_name='Filter by attribute names the forwarded pushed attributes',
blank=True,
to='attribute_aggregator.AttributeList',
null=True,
on_delete=models.CASCADE,
),
),
(
'attribute_list_for_sso_from_pull_sources',
models.ForeignKey(
related_name='attributes from pull sources',
verbose_name='Pull attributes list',
blank=True,
to='attribute_aggregator.AttributeList',
null=True,
on_delete=models.CASCADE,
),
),
(
'source_filter_for_sso_from_push_sources',
models.ManyToManyField(
related_name='filter attributes of push sources with sources',
null=True,
verbose_name='Filter by source the forwarded pushed attributes',
to='attribute_aggregator.AttributeSource',
blank=True,
),
),
],
options={
'verbose_name': 'attribute policy',
'verbose_name_plural': 'attribute policies',
},
bases=(models.Model,),
),
]

View File

@ -1,43 +0,0 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('idp', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='attributepolicy',
name='attribute_filter_for_sso_from_push_sources',
field=models.ForeignKey(
related_name='+',
verbose_name='Filter by attribute names the forwarded pushed attributes',
blank=True,
to='attribute_aggregator.AttributeList',
null=True,
on_delete=models.CASCADE,
),
),
migrations.AlterField(
model_name='attributepolicy',
name='attribute_list_for_sso_from_pull_sources',
field=models.ForeignKey(
related_name='+',
verbose_name='Pull attributes list',
blank=True,
to='attribute_aggregator.AttributeList',
null=True,
on_delete=models.CASCADE,
),
),
migrations.AlterField(
model_name='attributepolicy',
name='source_filter_for_sso_from_push_sources',
field=models.ManyToManyField(
to='attribute_aggregator.AttributeSource',
verbose_name='Filter by source the forwarded pushed attributes',
blank=True,
),
),
]

View File

@ -1,26 +0,0 @@
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('saml', '0016_auto_20150915_2041'),
('idp', '0002_auto_20150526_2239'),
]
operations = [
migrations.RemoveField(
model_name='attributepolicy',
name='attribute_filter_for_sso_from_push_sources',
),
migrations.RemoveField(
model_name='attributepolicy',
name='attribute_list_for_sso_from_pull_sources',
),
migrations.RemoveField(
model_name='attributepolicy',
name='source_filter_for_sso_from_push_sources',
),
migrations.DeleteModel(
name='AttributePolicy',
),
]

View File

@ -44,7 +44,7 @@ from urllib.parse import quote, urlencode
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import BACKEND_SESSION_KEY, REDIRECT_FIELD_NAME, get_user_model, load_backend
from django.contrib.auth import BACKEND_SESSION_KEY, REDIRECT_FIELD_NAME, load_backend
from django.contrib.auth.decorators import login_required
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseRedirect
@ -62,6 +62,7 @@ from authentic2 import views as a2_views
from authentic2.attributes_ng.engine import get_attributes
from authentic2.compat_lasso import lasso
from authentic2.constants import NONCE_FIELD_NAME
from authentic2.custom_user.models import User
from authentic2.idp.saml.common import kill_django_sessions
from authentic2.saml import saml2utils
from authentic2.saml.common import (
@ -118,8 +119,6 @@ from authentic2.views import passive_login
from . import app_settings
User = get_user_model()
logger = logging.getLogger(__name__)

View File

@ -1,23 +0,0 @@
# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.urls import re_path
from authentic2.idp.interactions import consent_federation
urlpatterns = [
re_path(r'^consent_federation', consent_federation, name='a2-consent-federation'),
]

View File

@ -20,7 +20,6 @@ import urllib.parse
from datetime import timedelta
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand
from django.db import transaction
from django.db.models import F, Q
@ -30,14 +29,13 @@ from authentic2 import app_settings
from authentic2.a2_rbac.models import OrganizationalUnit
from authentic2.backends import get_user_queryset
from authentic2.backends.ldap_backend import LDAPBackend
from authentic2.custom_user.models import User
from authentic2.journal_event_types import UserDeletionForInactivity, UserNotificationInactivity
from authentic2.utils import sms as utils_sms
from authentic2.utils.misc import get_password_authenticator, send_templated_mail
logger = logging.getLogger(__name__)
User = get_user_model()
class Command(BaseCommand):
help = '''Clean unused accounts'''

View File

@ -19,10 +19,10 @@ import json
import logging
import ldif
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand
from django.db.transaction import atomic
from authentic2.custom_user.models import User
from authentic2.hashers import olap_password_to_dj
from authentic2.models import Attribute
@ -44,7 +44,6 @@ class DjangoUserLDIFParser(ldif.LDIFParser):
ldif.LDIFParser.__init__(self, *args, **kwargs)
def handle(self, dn, entry):
User = get_user_model()
if self.object_class not in entry['objectClass']:
if self.verbosity >= 2:
self.command.stdout.write('Ignoring entry %r' % dn)

View File

@ -16,15 +16,13 @@
import getpass
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand, CommandError
from django.db import DEFAULT_DB_ALIAS
from authentic2.custom_user.models import User
from authentic2.models import PasswordReset
from authentic2.utils.misc import generate_password
User = get_user_model()
class Command(BaseCommand):
help = "Reset a user's password for django.contrib.auth."

View File

@ -20,11 +20,12 @@ import logging
import re
import sys
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand
from ldap.dn import escape_dn_chars
from ldif import LDIFWriter
from authentic2.custom_user.models import User
COMMAND = 1
ATTR = 2
@ -49,7 +50,7 @@ class Command(BaseCommand):
if command == 'SEARCH':
out = io.BytesIO()
ldif_writer = LDIFWriter(out)
qs = get_user_model().objects.all()
qs = User.objects.all()
if attrs['filter'] != '(objectClass=*)':
m = re.match(r'\((\w*)=(.*)\)', attrs['filter'])
if not m:

View File

@ -22,7 +22,6 @@ from collections import defaultdict
from io import StringIO
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.core.validators import validate_slug
@ -36,6 +35,7 @@ from authentic2 import app_settings as a2_app_settings
from authentic2.a2_rbac.models import OrganizationalUnit, Role
from authentic2.a2_rbac.utils import generate_slug, get_default_ou
from authentic2.custom_user.backends import DjangoRBACBackend
from authentic2.custom_user.models import User
from authentic2.forms.fields import (
CheckPasswordField,
CommaSeparatedCharField,
@ -57,7 +57,6 @@ from authentic2.validators import EmailValidator
from . import app_settings, fields, utils
User = get_user_model()
ChooseRolesField = fields.ChooseRolesField
logger = logging.getLogger(__name__)
@ -184,7 +183,7 @@ class UserEditForm(LimitQuerysetFormMixin, CssClass, BaseUserForm):
if a2_app_settings.A2_PHONE_IS_UNIQUE:
if (
AttributeValue.objects.filter(
content_type=ContentType.objects.get_for_model(get_user_model()),
content_type=ContentType.objects.get_for_model(User),
attribute=authn.phone_identifier_field,
)
.exclude(object_id=self.instance.id)
@ -194,7 +193,7 @@ class UserEditForm(LimitQuerysetFormMixin, CssClass, BaseUserForm):
elif getattr(self.instance.ou, 'phone_is_unique', False):
other_owners = (
AttributeValue.objects.filter(
content_type=ContentType.objects.get_for_model(get_user_model()),
content_type=ContentType.objects.get_for_model(User),
attribute=authn.phone_identifier_field,
)
.exclude(object_id=self.instance.id)

View File

@ -14,7 +14,6 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _
from authentic2.a2_rbac.models import Role
@ -24,11 +23,9 @@ from authentic2.backends.ldap_backend import (
LDAP_DEACTIVATION_REASON_NOT_PRESENT,
LDAP_DEACTIVATION_REASON_OLD_SOURCE,
)
from authentic2.custom_user.models import DeletedUser
from authentic2.custom_user.models import DeletedUser, User
from authentic2.journal_event_types import EventTypeWithService, get_attributes_label
User = get_user_model()
class ManagerUserCreation(EventTypeDefinition):
name = 'manager.user.creation'

View File

@ -18,7 +18,6 @@ import functools
import uuid
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.core.validators import EmailValidator
@ -33,12 +32,10 @@ from authentic2.apps.journal.forms import JournalForm as BaseJournalForm
from authentic2.apps.journal.models import EventType, n_2_pairing
from authentic2.apps.journal.search_engine import JournalSearchEngine as BaseJournalSearchEngine
from authentic2.apps.journal.views import JournalView
from authentic2.custom_user.models import DeletedUser
from authentic2.custom_user.models import DeletedUser, User
from . import views
User = get_user_model()
class JournalSearchEngine(BaseJournalSearchEngine):
def search_by_uuid(self, lexem):

View File

@ -14,14 +14,12 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib.auth import get_user_model
from import_export.fields import Field
from import_export.resources import ModelResource
from import_export.widgets import Widget
from authentic2.a2_rbac.models import Role
User = get_user_model()
from authentic2.custom_user.models import User
class ListWidget(Widget):

View File

@ -18,7 +18,6 @@ import json
from functools import reduce
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.paginator import EmptyPage, Paginator
@ -37,6 +36,7 @@ from authentic2 import data_transfer
from authentic2.a2_rbac.models import OrganizationalUnit, Permission, Role, RoleParenting
from authentic2.a2_rbac.utils import get_default_ou
from authentic2.apps.journal.views import JournalViewWithContext
from authentic2.custom_user.models import User
from authentic2.forms.profile import modelform_factory
from authentic2.role_summary import get_roles_summary_cache
from authentic2.utils import crypto, hooks
@ -46,8 +46,6 @@ from . import forms, resources, tables, views
from .journal_views import BaseJournalView
from .utils import has_show_username, label_from_role, label_from_user
User = get_user_model()
class RolesMixin:
service_roles = True
@ -609,7 +607,7 @@ class RoleRemoveAdminUserView(
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
self.user = get_user_model().objects.get(pk=kwargs['user_pk'])
self.user = User.objects.get(pk=kwargs['user_pk'])
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):

View File

@ -17,7 +17,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import django_tables2 as tables
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.db.models import CharField, OuterRef, Subquery
from django.db.models.expressions import RawSQL
@ -28,13 +27,12 @@ from django.utils.translation import gettext_noop
from django_tables2.utils import A
from authentic2.a2_rbac.models import OrganizationalUnit, Role
from authentic2.custom_user.models import User
from authentic2.middleware import StoreRequestMiddleware
from authentic2.models import AttributeValue, Service
from authentic2.utils.misc import get_password_authenticator
from authentic2_idp_oidc.models import OIDCAuthorization
User = get_user_model()
TABLES_MAJOR_VERSION = int(tables.__version__.split('.', maxsplit=1)[0])

View File

@ -20,10 +20,10 @@ import os
import uuid
import tablib
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.files.storage import default_storage
from authentic2.custom_user.models import User
from authentic2.manager.resources import UserResource
from authentic2.models import Attribute, AttributeValue
from authentic2.utils.misc import batch_queryset
@ -37,7 +37,7 @@ def get_user_dataset(qs):
at_mapping = {a.id: a for a in Attribute.objects.all()}
avs = (
AttributeValue.objects.filter(content_type=ContentType.objects.get_for_model(get_user_model()))
AttributeValue.objects.filter(content_type=ContentType.objects.get_for_model(User))
.filter(attribute__disabled=False)
.values()
)
@ -125,7 +125,7 @@ class UserExport:
def export_users_to_file(uuid, query):
export = UserExport(uuid)
qs = get_user_model().objects.all()
qs = User.objects.all()
qs.set_trigram_similarity_threshold()
qs.query = query
qs = qs.select_related('ou')

View File

@ -20,7 +20,7 @@ import datetime
import operator
from django.contrib import messages
from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import PermissionDenied
from django.core.mail import EmailMultiAlternatives
from django.db import models, transaction
@ -43,6 +43,7 @@ from authentic2.a2_rbac.models import OrganizationalUnit, Role, RoleParenting
from authentic2.a2_rbac.utils import get_default_ou
from authentic2.apps.journal.views import JournalViewWithContext
from authentic2.backends.ldap_backend import LDAPBackend
from authentic2.custom_user.models import User
from authentic2.models import Attribute, PasswordReset, Setting
from authentic2.utils import hooks, spooler, switch_user
from authentic2.utils.misc import (
@ -93,12 +94,10 @@ from .views import (
TitleMixin,
)
User = get_user_model()
class UsersView(HideOUColumnMixin, BaseTableView):
template_name = 'authentic2/manager/users.html'
model = get_user_model()
model = User
table_class = UserTable
permissions = ['custom_user.search_user']
search_form_class = UserSearchForm
@ -175,7 +174,7 @@ users = UsersView.as_view()
class UserAddView(ActionMixin, BaseAddView):
model = get_user_model()
model = User
title = _('Create user')
action = _('Create')
fields = [
@ -307,7 +306,7 @@ user_add_choose_ou = UserAddChooseOU.as_view()
class UserDetailView(OtherActionsMixin, BaseDetailView):
model = get_user_model()
model = User
fields = ['username', 'ou', 'first_name', 'last_name', 'email']
form_class = UserEditForm
template_name = 'authentic2/manager/user_detail.html'
@ -504,7 +503,7 @@ user_detail = UserDetailView.as_view()
class UserEditView(OtherActionsMixin, ActionMixin, BaseEditView):
model = get_user_model()
model = User
template_name = 'authentic2/manager/user_edit.html'
form_class = UserEditForm
permissions = ['custom_user.change_user']
@ -623,7 +622,7 @@ users_export_progress = UsersExportProgressView.as_view()
class UserChangePasswordView(ActionMixin, BaseEditView):
template_name = 'authentic2/manager/form.html'
model = get_user_model()
model = User
form_class = UserChangePasswordForm
permissions = ['custom_user.change_password_user']
title = _('Change user password')
@ -651,7 +650,7 @@ user_change_password = UserChangePasswordView.as_view()
class UserChangeEmailView(BaseEditView):
template_name = 'authentic2/manager/user_change_email.html'
model = get_user_model()
model = User
form_class = UserChangeEmailForm
permissions = ['custom_user.change_email_user']
success_url = '..'
@ -681,7 +680,7 @@ user_change_email = UserChangeEmailView.as_view()
class UserRolesView(HideOUColumnMixin, BaseSubTableView):
model = get_user_model()
model = User
form_class = ChooseUserRoleForm
search_form_class = UserRoleSearchForm
success_url = '.'
@ -778,7 +777,7 @@ roles = UserRolesView.as_view()
class UserDeleteView(BaseDeleteView):
model = get_user_model()
model = User
title = _('Delete user')
template_name = 'authentic2/manager/user_delete.html'
success_url = reverse_lazy('a2-manager-users')
@ -1004,7 +1003,7 @@ class UserAuthorizationsView(
permissions = ['custom_user.view_user']
template_name = 'authentic2/manager/user_authorizations.html'
title = pgettext_lazy('manager', 'Consent Management')
model = get_user_model()
model = User
table_class = UserAuthorizationsTable
form_class = ChooseUserAuthorizationsForm
success_url = '.'

View File

@ -19,11 +19,11 @@ import functools
import operator
import pickle
from django.contrib.auth import get_user_model
from django.utils.encoding import force_str
from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget
from authentic2.a2_rbac.models import Role
from authentic2.custom_user.models import User
from authentic2.utils import crypto
from authentic2_idp_oidc.models import OIDCAuthorization
@ -100,7 +100,7 @@ class SimpleModelSelect2MultipleWidget(Select2Mixin, ModelSelect2MultipleWidget)
class SearchUserWidgetMixin(SplitTermMixin):
model = get_user_model()
model = User
search_fields = [
'username__icontains',
'first_name__icontains',

View File

@ -3,7 +3,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0002_auto_20150323_1720'),
('contenttypes', '0001_initial'),
]
@ -98,7 +97,10 @@ class Migration(migrations.Migration):
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('creation', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
('user', models.ForeignKey(verbose_name='user', to='auth.User', on_delete=models.CASCADE)),
(
'user',
models.ForeignKey(verbose_name='user', to='custom_user.User', on_delete=models.CASCADE),
),
],
options={
'verbose_name': 'user to delete',
@ -183,7 +185,10 @@ class Migration(migrations.Migration):
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('user', models.ForeignKey(verbose_name='user', to='auth.User', on_delete=models.CASCADE)),
(
'user',
models.ForeignKey(verbose_name='user', to='custom_user.User', on_delete=models.CASCADE),
),
],
options={
'verbose_name': 'password reset',
@ -202,7 +207,10 @@ class Migration(migrations.Migration):
('external_id', models.CharField(max_length=256, verbose_name='external id')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
('updated', models.DateTimeField(auto_now=True, verbose_name='last update date')),
('user', models.ForeignKey(verbose_name='user', to='auth.User', on_delete=models.CASCADE)),
(
'user',
models.ForeignKey(verbose_name='user', to='custom_user.User', on_delete=models.CASCADE),
),
],
options={
'verbose_name': 'user external id',

View File

@ -1,10 +1,8 @@
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('authentic2', '0002_auto_20150320_1418'),
]
@ -12,25 +10,19 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='deleteduser',
name='user',
field=models.ForeignKey(
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
),
field=models.ForeignKey(verbose_name='user', to='custom_user.User', on_delete=models.CASCADE),
preserve_default=True,
),
migrations.AlterField(
model_name='passwordreset',
name='user',
field=models.ForeignKey(
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
),
field=models.ForeignKey(verbose_name='user', to='custom_user.User', on_delete=models.CASCADE),
preserve_default=True,
),
migrations.AlterField(
model_name='userexternalid',
name='user',
field=models.ForeignKey(
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
),
field=models.ForeignKey(verbose_name='user', to='custom_user.User', on_delete=models.CASCADE),
preserve_default=True,
),
]

View File

@ -1,4 +1,3 @@
from django.conf import settings
from django.db import migrations, models
@ -12,7 +11,7 @@ class Migration(migrations.Migration):
model_name='passwordreset',
name='user',
field=models.ForeignKey(
verbose_name='user', to=settings.AUTH_USER_MODEL, unique=True, on_delete=models.CASCADE
verbose_name='user', to='custom_user.User', unique=True, on_delete=models.CASCADE
),
preserve_default=True,
),

View File

@ -1,4 +1,3 @@
from django.conf import settings
from django.db import migrations, models
@ -11,8 +10,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='passwordreset',
name='user',
field=models.OneToOneField(
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
),
field=models.OneToOneField(verbose_name='user', to='custom_user.User', on_delete=models.CASCADE),
),
]

View File

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentic2', '0015_auto_20160621_1711'),
('custom_user', '0011_manual_attribute_values_for_name_fields'),
]
operations = [

View File

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('custom_user', '0016_auto_20180925_1107'),
('authentic2', '0022_attribute_scopes'),
]

View File

@ -5,7 +5,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('custom_user', '0016_auto_20180925_1107'),
('authentic2', '0023_auto_20181031_0900'),
]

View File

@ -1,7 +1,6 @@
# Generated by Django 1.11.20 on 2019-10-09 08:47
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
@ -17,7 +16,7 @@ class Migration(migrations.Migration):
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name='deletion',
to=settings.AUTH_USER_MODEL,
to='custom_user.User',
verbose_name='user',
),
),

View File

@ -16,11 +16,6 @@ def fill_deleted(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [
('authentic2', '0026_token'),
('custom_user', '0019_add_user_deleted'),
]
run_before = [
('custom_user', '0026_remove_user_deleted'),
]
operations = [

View File

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('custom_user', '0030_auto_20220304_1136'),
('authentic2', '0035_service_home_url'),
]

View File

@ -9,7 +9,6 @@ import authentic2.a2_rbac.utils
class Migration(migrations.Migration):
dependencies = [
('a2_rbac', '0036_delete_roleattribute'),
('authentic2', '0045_smscode'),
]

View File

@ -1,13 +1,11 @@
# Generated by Django 2.2.26 on 2023-01-17 14:13
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('authentic2', '0047_initialize_services_runtime_settings'),
]
@ -23,7 +21,7 @@ class Migration(migrations.Migration):
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
to='custom_user.User',
verbose_name='user',
),
),

View File

@ -0,0 +1,393 @@
# Generated by Django 3.2.23 on 2023-12-14 21:01
import uuid
import django.contrib.postgres.fields
import django.contrib.postgres.search
import django.db.models.manager
from django.db import migrations, models
import authentic2.utils.sms
import authentic2.validators
class Migration(migrations.Migration):
replaces = [
('authentic2', '0001_initial'),
('authentic2', '0002_auto_20150320_1418'),
('authentic2', '0003_auto_20150409_1840'),
('authentic2', '0004_service'),
('authentic2', '0005_service_ou'),
('authentic2', '0006_conditional_slug_index'),
('authentic2', '0007_auto_20150523_0028'),
('authentic2', '0008_auto_20160204_1415'),
('authentic2', '0009_auto_20160211_2247'),
('authentic2', '0010_attributevalue_multiple'),
('authentic2', '0011_auto_20160211_2253'),
('authentic2', '0012_auto_20160211_2255'),
('authentic2', '0013_auto_20160211_2258'),
('authentic2', '0014_attributevalue_verified'),
('authentic2', '0015_auto_20160621_1711'),
('authentic2', '0016_attribute_disabled'),
('authentic2', '0017_modify_attribute_serialization'),
('authentic2', '0018_auto_20170524_0842'),
('authentic2', '0019_auto_20170309_1529'),
('authentic2', '0020_delete_federatedid'),
('authentic2', '0021_attribute_order'),
('authentic2', '0022_attribute_scopes'),
('authentic2', '0023_auto_20181031_0900'),
('authentic2', '0024_auto_20190617_1113'),
('authentic2', '0025_auto_20191009_1047'),
('authentic2', '0026_token'),
('authentic2', '0027_remove_deleteduser'),
('authentic2', '0028_trigram_unaccent_index'),
('authentic2', '0029_auto_20201013_1614'),
('authentic2', '0030_clean_admin_tools_tables'),
('authentic2', '0031_add_search_vector_to_attributes'),
('authentic2', '0032_initialize_search_vectors'),
('authentic2', '0033_recreate_immutable_unaccent'),
('authentic2', '0034_attribute_required_on_login'),
('authentic2', '0035_service_home_url'),
('authentic2', '0036_service_profile_types'),
('authentic2', '0037_auto_20220331_1513'),
('authentic2', '0038_make_service_ou_non_null'),
('authentic2', '0039_add_unique_attribute_constraint'),
('authentic2', '0040_add_external_guid'),
('authentic2', '0041_lock'),
('authentic2', '0042_api_client'),
('authentic2', '0043_api_client_description'),
('authentic2', '0044_apiclient_ou'),
('authentic2', '0044_auto_20220530_1426'),
('authentic2', '0045_auto_20221222_1013'),
('authentic2', '0045_auto_20230117_1513'),
('authentic2', '0045_smscode'),
('authentic2', '0046_runtimesetting'),
('authentic2', '0047_initialize_services_runtime_settings'),
('authentic2', '0048_rename_services_runtime_settings'),
('authentic2', '0049_apiclient_allowed_user_attributes'),
('authentic2', '0050_initialize_users_advanced_configuration'),
]
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name='APIClient',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('name', models.CharField(max_length=128, verbose_name='Name')),
('description', models.TextField(blank=True, verbose_name='Description')),
('identifier', models.CharField(max_length=256, verbose_name='Identifier')),
('password', models.CharField(max_length=256, verbose_name='Password')),
(
'restrict_to_anonymised_data',
models.BooleanField(default=False, verbose_name='Restrict to anonymised data'),
),
],
options={
'verbose_name': 'APIClient',
'verbose_name_plural': 'APIClient',
},
),
migrations.CreateModel(
name='Attribute',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('label', models.CharField(max_length=63, unique=True, verbose_name='label')),
('description', models.TextField(blank=True, verbose_name='description')),
('name', models.SlugField(max_length=256, unique=True, verbose_name='name')),
('required', models.BooleanField(blank=True, default=False, verbose_name='required')),
(
'asked_on_registration',
models.BooleanField(blank=True, default=False, verbose_name='asked on registration'),
),
(
'user_editable',
models.BooleanField(blank=True, default=False, verbose_name='user editable'),
),
('user_visible', models.BooleanField(blank=True, default=False, verbose_name='user visible')),
('multiple', models.BooleanField(blank=True, default=False, verbose_name='multiple')),
('kind', models.CharField(max_length=16, verbose_name='kind')),
('disabled', models.BooleanField(blank=True, default=False, verbose_name='disabled')),
('searchable', models.BooleanField(blank=True, default=False, verbose_name='searchable')),
(
'required_on_login',
models.BooleanField(blank=True, default=False, verbose_name='required on login'),
),
(
'scopes',
models.CharField(
blank=True,
default='',
help_text='scopes separated by spaces',
max_length=256,
verbose_name='scopes',
),
),
('order', models.PositiveIntegerField(default=0, verbose_name='order')),
],
options={
'verbose_name': 'attribute definition',
'verbose_name_plural': 'attribute definitions',
'ordering': ('order', 'id'),
'base_manager_name': 'all_objects',
},
managers=[
('all_objects', django.db.models.manager.Manager()),
],
),
migrations.CreateModel(
name='AttributeValue',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('object_id', models.PositiveIntegerField(db_index=True, verbose_name='object identifier')),
('multiple', models.BooleanField(default=False, null=True)),
('content', models.TextField(db_index=True, verbose_name='content')),
(
'search_vector',
django.contrib.postgres.search.SearchVectorField(editable=False, null=True),
),
('verified', models.BooleanField(default=False)),
(
'verification_sources',
django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(max_length=63),
null=True,
size=None,
verbose_name='verification sources',
),
),
(
'last_verified_on',
models.DateTimeField(null=True, verbose_name='last verification timestamp'),
),
],
options={
'verbose_name': 'attribute value',
'verbose_name_plural': 'attribute values',
'ordering': ('attribute__order', 'id'),
},
managers=[
('all_objects', django.db.models.manager.Manager()),
],
),
migrations.CreateModel(
name='AuthenticationEvent',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('when', models.DateTimeField(auto_now=True, verbose_name='when')),
('who', models.CharField(max_length=80, verbose_name='who')),
('how', models.CharField(max_length=32, verbose_name='how')),
('nonce', models.CharField(max_length=255, verbose_name='nonce')),
],
options={
'verbose_name': 'authentication log',
'verbose_name_plural': 'authentication logs',
},
),
migrations.CreateModel(
name='AuthorizedRole',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
],
),
migrations.CreateModel(
name='Lock',
fields=[
('created', models.DateTimeField(auto_now_add=True, verbose_name='Creation date')),
('name', models.TextField(primary_key=True, serialize=False, verbose_name='Name')),
],
options={
'verbose_name': 'Lock',
'verbose_name_plural': 'Lock',
},
),
migrations.CreateModel(
name='LogoutUrl',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
(
'logout_url',
models.URLField(
blank=True,
help_text='you can use a {} to pass the URL of the success icon, ex.: http://example.com/logout?next={}',
max_length=255,
null=True,
verbose_name='url',
),
),
(
'logout_use_iframe',
models.BooleanField(
default=False, verbose_name='use an iframe instead of an img tag for logout'
),
),
(
'logout_use_iframe_timeout',
models.PositiveIntegerField(
default=300,
help_text="if iframe logout is used, it's the time between the onload event for this iframe and the moment we "
'consider its loading to be really finished',
verbose_name='iframe logout timeout (ms)',
),
),
('object_id', models.PositiveIntegerField(verbose_name='object identifier')),
],
options={
'verbose_name': 'logout URL',
'verbose_name_plural': 'logout URL',
},
),
migrations.CreateModel(
name='PasswordReset',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
],
options={
'verbose_name': 'password reset',
'verbose_name_plural': 'password reset',
},
),
migrations.CreateModel(
name='Service',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('name', models.CharField(max_length=128, verbose_name='name')),
('slug', models.SlugField(max_length=128, verbose_name='slug')),
(
'unauthorized_url',
models.URLField(
blank=True, max_length=256, null=True, verbose_name='callback url when unauthorized'
),
),
('home_url', models.URLField(blank=True, max_length=256, null=True, verbose_name='Home URL')),
('logo', models.ImageField(blank=True, upload_to='services/logos', verbose_name='Logo')),
(
'colour',
models.CharField(
blank=True,
max_length=32,
null=True,
validators=[authentic2.validators.HexaColourValidator()],
verbose_name='Colour',
),
),
],
options={
'verbose_name': 'base service model',
'verbose_name_plural': 'base service models',
'base_manager_name': 'objects',
},
),
migrations.CreateModel(
name='Setting',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('key', models.CharField(max_length=128, unique=True, verbose_name='key')),
('value', models.JSONField(blank=True, verbose_name='value')),
],
),
migrations.CreateModel(
name='SMSCode',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
(
'value',
models.CharField(
default=authentic2.utils.sms.create_sms_code,
editable=False,
max_length=32,
verbose_name='Identifier',
),
),
('kind', models.CharField(max_length=32, verbose_name='Kind')),
(
'phone',
models.CharField(
blank=True,
max_length=64,
null=True,
validators=[authentic2.validators.PhoneNumberValidator],
verbose_name='phone number',
),
),
('url_token', models.UUIDField(default=uuid.uuid4, verbose_name='URL token')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Creation date')),
('expires', models.DateTimeField(verbose_name='Expires')),
('sent', models.BooleanField(default=False, verbose_name='SMS code sent')),
('fake', models.BooleanField(default=False, verbose_name='Is a fake code')),
],
),
migrations.CreateModel(
name='Token',
fields=[
(
'uuid',
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
verbose_name='Identifier',
),
),
('kind', models.CharField(max_length=32, verbose_name='Kind')),
('content', models.JSONField(blank=True, verbose_name='Content')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='Creation date')),
('expires', models.DateTimeField(verbose_name='Expires')),
],
options={
'ordering': ('-expires', 'kind', 'uuid'),
},
),
migrations.CreateModel(
name='UserExternalId',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('source', models.CharField(max_length=256, verbose_name='source')),
('external_id', models.CharField(max_length=256, null=True, verbose_name='external id')),
('external_guid', models.UUIDField(null=True, verbose_name='External GUID')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
('updated', models.DateTimeField(auto_now=True, verbose_name='last update date')),
],
options={
'verbose_name': 'user external id',
'verbose_name_plural': 'user external ids',
},
),
]

View File

@ -0,0 +1,292 @@
# Generated by Django 3.2.23 on 2023-12-14 21:01
import django.contrib.postgres.indexes
import django.db.models.deletion
from django.db import migrations, models
import authentic2.a2_rbac.utils
def create_trigger(apps, schema_editor):
with schema_editor.connection.cursor() as cursor:
cursor.execute('SHOW default_text_search_config')
assert cursor.fetchone()
cursor.execute(
'''CREATE OR REPLACE FUNCTION authentic2_update_atv_search_vector() RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND NEW.content <> OLD.content) THEN
NEW.search_vector = to_tsvector(NEW.content);
END IF;
RETURN NEW;
END; $$ LANGUAGE plpgsql'''
)
cursor.execute(
'''CREATE TRIGGER authentic2_attributevalue_search_vector_trigger
BEFORE INSERT OR UPDATE OF content
ON authentic2_attributevalue
FOR EACH ROW EXECUTE PROCEDURE authentic2_update_atv_search_vector()'''
)
def drop_trigger(apps, schema_editor):
with schema_editor.connection.cursor() as cursor:
cursor.execute(
'DROP TRIGGER IF EXISTS authentic2_attributevalue_search_vector_trigger ON'
' authentic2_attributevalue'
)
cursor.execute('DROP FUNCTION IF EXISTS authentic2_update_atv_search_vector')
def initialize_services_runtime_settings(apps, schema_editor):
from authentic2.utils.misc import RUNTIME_SETTINGS
Setting = apps.get_model('authentic2', 'Setting')
for key, data in RUNTIME_SETTINGS.items():
Setting.objects.get_or_create(
key=key,
defaults={
'value': data['value'],
},
)
class Migration(migrations.Migration):
replaces = [
('authentic2', '0001_initial'),
('authentic2', '0002_auto_20150320_1418'),
('authentic2', '0003_auto_20150409_1840'),
('authentic2', '0004_service'),
('authentic2', '0005_service_ou'),
('authentic2', '0006_conditional_slug_index'),
('authentic2', '0007_auto_20150523_0028'),
('authentic2', '0008_auto_20160204_1415'),
('authentic2', '0009_auto_20160211_2247'),
('authentic2', '0010_attributevalue_multiple'),
('authentic2', '0011_auto_20160211_2253'),
('authentic2', '0012_auto_20160211_2255'),
('authentic2', '0013_auto_20160211_2258'),
('authentic2', '0014_attributevalue_verified'),
('authentic2', '0015_auto_20160621_1711'),
('authentic2', '0016_attribute_disabled'),
('authentic2', '0017_modify_attribute_serialization'),
('authentic2', '0018_auto_20170524_0842'),
('authentic2', '0019_auto_20170309_1529'),
('authentic2', '0020_delete_federatedid'),
('authentic2', '0021_attribute_order'),
('authentic2', '0022_attribute_scopes'),
('authentic2', '0023_auto_20181031_0900'),
('authentic2', '0024_auto_20190617_1113'),
('authentic2', '0025_auto_20191009_1047'),
('authentic2', '0026_token'),
('authentic2', '0027_remove_deleteduser'),
('authentic2', '0028_trigram_unaccent_index'),
('authentic2', '0029_auto_20201013_1614'),
('authentic2', '0030_clean_admin_tools_tables'),
('authentic2', '0031_add_search_vector_to_attributes'),
('authentic2', '0032_initialize_search_vectors'),
('authentic2', '0033_recreate_immutable_unaccent'),
('authentic2', '0034_attribute_required_on_login'),
('authentic2', '0035_service_home_url'),
('authentic2', '0036_service_profile_types'),
('authentic2', '0037_auto_20220331_1513'),
('authentic2', '0038_make_service_ou_non_null'),
('authentic2', '0039_add_unique_attribute_constraint'),
('authentic2', '0040_add_external_guid'),
('authentic2', '0041_lock'),
('authentic2', '0042_api_client'),
('authentic2', '0043_api_client_description'),
('authentic2', '0044_apiclient_ou'),
('authentic2', '0044_auto_20220530_1426'),
('authentic2', '0045_auto_20221222_1013'),
('authentic2', '0045_auto_20230117_1513'),
('authentic2', '0045_smscode'),
('authentic2', '0046_runtimesetting'),
('authentic2', '0047_initialize_services_runtime_settings'),
('authentic2', '0048_rename_services_runtime_settings'),
('authentic2', '0049_apiclient_allowed_user_attributes'),
('authentic2', '0050_initialize_users_advanced_configuration'),
]
initial = True
dependencies = [
('a2_rbac', '0041_replace'),
('custom_user', '0037_replace'),
('contenttypes', '0002_remove_content_type_name'),
('authentic2', '0051_replace'),
]
operations = [
migrations.CreateModel(
name='LDAPUser',
fields=[],
options={
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('custom_user.user',),
),
migrations.AddField(
model_name='userexternalid',
name='user',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to='custom_user.user', verbose_name='user'
),
),
migrations.AddField(
model_name='smscode',
name='user',
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
to='custom_user.user',
verbose_name='user',
),
),
migrations.AddField(
model_name='service',
name='authorized_roles',
field=models.ManyToManyField(
blank=True,
related_name='allowed_services',
through='authentic2.AuthorizedRole',
to='a2_rbac.Role',
verbose_name='authorized services',
),
),
migrations.AddField(
model_name='service',
name='ou',
field=models.ForeignKey(
default=authentic2.a2_rbac.utils.get_default_ou_pk,
on_delete=django.db.models.deletion.CASCADE,
to='a2_rbac.organizationalunit',
verbose_name='organizational unit',
),
),
migrations.AddField(
model_name='service',
name='profile_types',
field=models.ManyToManyField(
blank=True,
related_name='_authentic2_service_profile_types_+',
through='custom_user.ServiceProfileType',
to='custom_user.ProfileType',
verbose_name='allowed services for this profile type',
),
),
migrations.AddField(
model_name='passwordreset',
name='user',
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE, to='custom_user.user', verbose_name='user'
),
),
migrations.AddField(
model_name='logouturl',
name='content_type',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to='contenttypes.contenttype',
verbose_name='content type',
),
),
migrations.AddField(
model_name='authorizedrole',
name='role',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='a2_rbac.role'),
),
migrations.AddField(
model_name='authorizedrole',
name='service',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='authentic2.service'),
),
migrations.AddField(
model_name='attributevalue',
name='attribute',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to='authentic2.attribute',
verbose_name='attribute',
),
),
migrations.AddField(
model_name='attributevalue',
name='content_type',
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to='contenttypes.contenttype',
verbose_name='content type',
),
),
migrations.AddField(
model_name='apiclient',
name='allowed_user_attributes',
field=models.ManyToManyField(
blank=True,
related_name='apiclients',
to='authentic2.Attribute',
verbose_name='allowed user attributes',
),
),
migrations.AddField(
model_name='apiclient',
name='apiclient_roles',
field=models.ManyToManyField(
blank=True, related_name='apiclients', to='a2_rbac.Role', verbose_name='roles'
),
),
migrations.AddField(
model_name='apiclient',
name='ou',
field=models.ForeignKey(
blank=True,
default=authentic2.a2_rbac.utils.get_default_ou_pk,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to='a2_rbac.organizationalunit',
verbose_name='organizational unit',
),
),
migrations.AddConstraint(
model_name='userexternalid',
constraint=models.CheckConstraint(
check=models.Q(
('external_id__isnull', False), ('external_guid__isnull', False), _connector='OR'
),
name='at_least_one_id',
),
),
migrations.AlterUniqueTogether(
name='userexternalid',
unique_together={('source', 'external_id'), ('source', 'external_guid')},
),
migrations.AlterUniqueTogether(
name='service',
unique_together={('slug', 'ou')},
),
migrations.AddIndex(
model_name='attributevalue',
index=django.contrib.postgres.indexes.GinIndex(
fields=['search_vector'], name='authentic2_atv_tsvector_idx'
),
),
migrations.AddConstraint(
model_name='attributevalue',
constraint=models.UniqueConstraint(
condition=models.Q(('multiple', False)),
fields=('content_type', 'object_id', 'attribute'),
name='unique_attribute_idx',
),
),
migrations.AlterUniqueTogether(
name='attributevalue',
unique_together={('content_type', 'object_id', 'attribute', 'multiple', 'content')},
),
migrations.RunPython(create_trigger, drop_trigger),
migrations.RunPython(
initialize_services_runtime_settings,
reverse_code=migrations.RunPython.noop,
),
]

View File

@ -53,7 +53,7 @@ from .utils.sms import create_sms_code
class UserExternalId(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.CASCADE)
user = models.ForeignKey('custom_user.User', verbose_name=_('user'), on_delete=models.CASCADE)
source = models.CharField(max_length=256, verbose_name=_('source'))
external_id = models.CharField(max_length=256, verbose_name=_('external id'), null=True)
external_guid = models.UUIDField(verbose_name=_('External GUID'), null=True)
@ -435,7 +435,7 @@ class AttributeValue(models.Model):
class PasswordReset(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.CASCADE)
user = models.OneToOneField('custom_user.User', verbose_name=_('user'), on_delete=models.CASCADE)
def save(self, *args, **kwargs):
if self.user_id and not self.user.has_usable_password():
@ -836,9 +836,7 @@ class SMSCode(models.Model):
phone = models.CharField(
_('phone number'), null=True, blank=True, max_length=64, validators=[PhoneNumberValidator]
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.CASCADE, null=True
)
user = models.ForeignKey('custom_user.User', verbose_name=_('user'), on_delete=models.CASCADE, null=True)
url_token = models.UUIDField(
verbose_name=_('URL token'),
default=uuid.uuid4,

View File

@ -1,6 +0,0 @@
Nonce are value which should be used only once for a certain period or
eventually forever. The nonce application allows any Django application to
implement this behaviour, by taking care of the storage implementation to keep around invalidated nonce.
For nonce which should not be kept forever the application also provide a
cleanup_nonce() function to delete the no longer invalid nonces.

View File

@ -1,19 +0,0 @@
# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from authentic2.nonce.utils import accept_nonce, cleanup_nonces
__all__ = ('accept_nonce', 'cleanup_nonces')

View File

@ -0,0 +1,13 @@
# Generated by Django 3.2.18 on 2023-12-13 20:00
from django.db import migrations
class Migration(migrations.Migration):
replaces = [('nonce', '0001_initial'), ('nonce', '0002_delete_nonce')]
initial = True
dependencies = []
operations = []

View File

@ -0,0 +1,15 @@
# Generated by Django 3.2.18 on 2023-12-13 19:03
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('nonce', '0001_initial'),
]
operations = [
migrations.DeleteModel(
name='Nonce',
),
]

View File

@ -1,39 +0,0 @@
# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.db import models
from django.utils import timezone
__all__ = ('Nonce',)
_NONCE_LENGTH_CONSTANT = 256
class NonceManager(models.Manager):
def cleanup(self, now=None):
now = now or timezone.now()
self.filter(not_on_or_after__lt=now).delete()
class Nonce(models.Model):
value = models.CharField(max_length=_NONCE_LENGTH_CONSTANT)
context = models.CharField(max_length=_NONCE_LENGTH_CONSTANT, blank=True, null=True)
not_on_or_after = models.DateTimeField(blank=True, null=True)
objects = NonceManager()
def __str__(self):
return str(self.value)

View File

@ -1,200 +0,0 @@
# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime as dt
import errno
import glob
import os.path
import tempfile
from calendar import timegm
from django.conf import settings
__all__ = ('accept_nonce', 'cleanup_nonces')
STORAGE_MODEL = 'model'
STORAGE_FILESYSTEM = 'fs:'
def compute_not_on_or_after(now, not_on_or_after):
try: # first try integer semantic
seconds = int(not_on_or_after)
not_on_or_after = now + dt.timedelta(seconds=seconds)
except ValueError:
try: # try timedelta semantic
not_on_or_after = now + not_on_or_after
except TypeError: # datetime semantic
pass
return not_on_or_after
# For the nonce filesystem storage the policy is to catch any OSerror when
# errno == ENOENT and to handle this case gracefully as there can be many race
# condition errors. But any other OSError is problematic and should be
# reported to the administrator by mail and so we let it unroll the stack
def unlink_if_exists(path):
try:
os.unlink(path)
except OSError as e:
if e.errno != errno.ENOENT:
raise
def accept_nonce_file_storage(path, now, value, context=None, not_on_or_after=None):
"""
Use a directory as a storage for nonce-context values. The last
modification time is used to store the expiration timestamp.
"""
now = dt.datetime.now()
filename = '%s_%s' % (value.encode('base64'), context.encode('base64'))
file_path = os.path.join(path, filename)
# test if the file exists
try:
stat = os.stat(file_path)
except OSError as e:
# test if it doesnt exit or if another error happened
if e.errno != errno.ENOENT:
raise
else:
# if it exists, test if it is too old
if stat.st_mtime < timegm(now.utctimetuple()):
# if it is too old delete it
unlink_if_exists(file_path)
else:
# not too old, the nonce is unacceptable
return False
# pylint: disable=consider-using-with
temp_file = tempfile.NamedTemporaryFile(dir=path, delete=False)
temp_file.close()
if not_on_or_after:
not_on_or_after = compute_not_on_or_after(now, not_on_or_after)
mtime = timegm(not_on_or_after.utctimetuple())
else:
mtime = 0x7FFF
try:
os.utime(temp_file.name, (mtime, mtime))
except OSError:
unlink_if_exists(temp_file.name)
raise
try:
os.link(temp_file.name, file_path)
except OSError as e:
if e.errno == errno.EEXIST:
unlink_if_exists(temp_file.name)
return False
return True
def accept_nonce_model(now, value, context=None, not_on_or_after=None):
from authentic2.nonce.models import Nonce
if not_on_or_after:
not_on_or_after = compute_not_on_or_after(now, not_on_or_after)
nonce, created = Nonce.objects.get_or_create(value=value, context=context)
if created or (nonce.not_on_or_after and nonce.not_on_or_after < now):
nonce.not_on_or_after = not_on_or_after
nonce.save()
return True
else:
return False
def cleanup_nonces_file_storage(dir_path, now):
for nonce_path in glob.iglob(os.path.join(dir_path, '*')):
now_time = timegm(now.utctimetuple())
try:
stat = os.stat(nonce_path)
except OSError as e:
if e.errno == errno.ENOENT:
continue
raise
if stat.st_mtime < now_time:
try:
os.unlink(nonce_path)
except OSError as e:
if e.errno == errno.ENOENT:
continue
raise
def cleanup_nonces(now=None):
"""
Cleanup stored nonce whose timestamp has expired, i.e.
nonce.not_on_or_after < now.
:param now:
a datetime value to define what is the current time, if None is
given, datetime.now() is used. It can be used for unit testing.
"""
from authentic2.nonce.models import Nonce
now = now or dt.datetime.now()
mode = getattr(settings, 'NONCE_STORAGE', STORAGE_MODEL)
# the model always exists, so we always clean it
Nonce.objects.cleanup(now)
if mode == STORAGE_MODEL:
pass
if mode.startswith(STORAGE_FILESYSTEM):
dir_path = mode[len(STORAGE_FILESYSTEM) :]
return cleanup_nonces_file_storage(dir_path, now)
else:
raise ValueError('Invalid NONCE_STORAGE setting: %r' % mode)
def accept_nonce(value, context=None, not_on_or_after=None, now=None):
"""
Verify that the given nonce value has not already been seen in the
context. If not, remember it for ever or until not_on_or_after if is
not None.
Depending on the backend storage used there can be limitation on the
acceptable length for the value and the context. For example the model
storage backend limits the length of those strings to 256 bytes.
:param value:
a string representing a nonce value.
:param context:
a string giving context to the nonce value
:param not_on_or_after:
an integer, a datetime.timedelta or datetime.datetime value. If not
none it is used to compute the expiration time for remembering this
nonce value. If an integer is given it is interpreted as relative
number of seconds since now, if a timedelta object is given it is
used as an offset from now, and if a datetime is given it is used
as an absolute value for the expiration time.
:param now:
a datetime value to define what is the current time, if None is
given, datetime.now() is used. It can be used for unit testing.
:returns:
a boolean, if True the nonce has never been seen before, or it
expired since the last time seen, otherwise the nonce has already
been seen and is invalid.
"""
now = now or dt.datetime.now()
mode = getattr(settings, 'NONCE_STORAGE', STORAGE_MODEL)
if mode == STORAGE_MODEL:
return accept_nonce_model(now, value, context=context, not_on_or_after=not_on_or_after)
elif mode.startswith(STORAGE_FILESYSTEM):
dir_path = mode[len(STORAGE_FILESYSTEM) :]
return accept_nonce_file_storage(
dir_path, now, value, context=context, not_on_or_after=not_on_or_after
)
else:
raise ValueError('Invalid NONCE_STORAGE setting: %r' % mode)

View File

@ -7,8 +7,6 @@ import authentic2.saml.models
class Migration(migrations.Migration):
dependencies = [
('auth', '__first__'),
('idp', '__first__'),
('contenttypes', '0001_initial'),
]
@ -474,17 +472,6 @@ class Migration(migrations.Migration):
'users_can_manage_federations',
models.BooleanField(default=True, verbose_name='users can manage federation'),
),
(
'attribute_policy',
models.ForeignKey(
related_name='attribute_policy',
on_delete=django.db.models.deletion.SET_NULL,
verbose_name='attribute policy',
blank=True,
to='idp.AttributePolicy',
null=True,
),
),
],
options={
'verbose_name': 'SAML service provider',
@ -757,7 +744,7 @@ class Migration(migrations.Migration):
model_name='libertyfederation',
name='user',
field=models.ForeignKey(
on_delete=django.db.models.deletion.SET_NULL, blank=True, to='auth.User', null=True
on_delete=django.db.models.deletion.SET_NULL, blank=True, to='custom_user.User', null=True
),
preserve_default=True,
),

View File

@ -1,11 +1,9 @@
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('saml', '0003_merge'),
]
@ -16,7 +14,7 @@ class Migration(migrations.Migration):
field=models.ForeignKey(
on_delete=django.db.models.deletion.SET_NULL,
blank=True,
to=settings.AUTH_USER_MODEL,
to='custom_user.User',
null=True,
),
preserve_default=True,

View File

@ -3,7 +3,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentic2', '0004_service'),
('saml', '0004_auto_20150410_1438'),
]

View File

@ -56,10 +56,6 @@ class Migration(migrations.Migration):
migrations.DeleteModel(
name='LibertyIdentityProvider',
),
migrations.RemoveField(
model_name='libertyserviceprovider',
name='attribute_policy',
),
migrations.RemoveField(
model_name='libertyserviceprovider',
name='enable_following_attribute_policy',

View File

@ -0,0 +1,417 @@
# Generated by Django 3.2.23 on 2023-12-14 21:01
import django.db.models.deletion
from django.db import migrations, models
import authentic2.saml.fields
import authentic2.saml.models
class Migration(migrations.Migration):
replaces = [
('saml', '0001_initial'),
('saml', '0002_auto_20150320_1245'),
('saml', '0002_ease_federation_migration'),
('saml', '0003_merge'),
('saml', '0004_auto_20150410_1438'),
('saml', '0005_make_liberty_provider_inherit_from_service'),
('saml', '0006_restore_foreign_keys'),
('saml', '0007_copy_service_ptr_id_to_old_id'),
('saml', '0008_alter_foreign_keys'),
('saml', '0009_auto'),
('saml', '0010_auto'),
('saml', '0011_auto'),
('saml', '0012_auto_20150526_2239'),
('saml', '0013_auto_20150617_1004'),
('saml', '0014_auto_20150617_1216'),
('saml', '0015_auto_20150915_2032'),
('saml', '0016_auto_20150915_2041'),
('saml', '0017_auto_20170710_1738'),
('saml', '0018_truncate_saml_keyvalue'),
('saml', '0019_auto_20200621_1558'),
('saml', '0020_libertysession_saml_libert_provide_39bb6c_idx'),
]
initial = True
dependencies = [
('authentic2', '0052_replace'),
('custom_user', '0037_replace'),
('contenttypes', '0002_remove_content_type_name'),
]
operations = [
migrations.CreateModel(
name='KeyValue',
fields=[
('key', models.CharField(max_length=128, primary_key=True, serialize=False)),
('value', authentic2.saml.fields.PickledObjectField()),
('created', models.DateTimeField(auto_now_add=True)),
],
options={
'verbose_name': 'key value association',
'verbose_name_plural': 'key value associations',
},
),
migrations.CreateModel(
name='LibertyArtifact',
fields=[
('creation', models.DateTimeField(auto_now_add=True)),
('artifact', models.CharField(max_length=128, primary_key=True, serialize=False)),
('content', models.TextField()),
('provider_id', models.CharField(max_length=256)),
],
options={
'verbose_name': 'SAML artifact',
'verbose_name_plural': 'SAML artifacts',
},
),
migrations.CreateModel(
name='LibertyFederation',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
(
'name_id_format',
models.CharField(blank=True, max_length=100, null=True, verbose_name='NameIDFormat'),
),
('name_id_content', models.CharField(max_length=100, verbose_name='NameID')),
(
'name_id_qualifier',
models.CharField(blank=True, max_length=256, null=True, verbose_name='NameQualifier'),
),
(
'name_id_sp_name_qualifier',
models.CharField(blank=True, max_length=256, null=True, verbose_name='SPNameQualifier'),
),
('termination_notified', models.BooleanField(blank=True, default=False)),
('creation', models.DateTimeField(auto_now_add=True)),
('last_modification', models.DateTimeField(auto_now=True)),
(
'user',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to='custom_user.user',
),
),
],
options={
'verbose_name': 'SAML federation',
'verbose_name_plural': 'SAML federations',
},
),
migrations.CreateModel(
name='LibertyProvider',
fields=[
(
'service_ptr',
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to='authentic2.service',
),
),
('entity_id', models.URLField(max_length=256, unique=True, verbose_name='Entity ID')),
(
'entity_id_sha1',
models.CharField(blank=True, max_length=40, verbose_name='Entity ID SHA1'),
),
('metadata_url', models.URLField(blank=True, max_length=256, verbose_name='Metadata URL')),
(
'protocol_conformance',
models.IntegerField(choices=[(3, 'SAML 2.0')], verbose_name='Protocol conformance'),
),
('metadata', models.TextField(validators=[authentic2.saml.models.metadata_validator])),
(
'federation_source',
models.CharField(blank=True, max_length=64, null=True, verbose_name='Federation source'),
),
],
options={
'verbose_name': 'SAML provider',
'verbose_name_plural': 'SAML providers',
'ordering': ('service_ptr__name',),
},
bases=('authentic2.service',),
),
migrations.CreateModel(
name='SPOptionsIdPPolicy',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('name', models.CharField(max_length=80, unique=True, verbose_name='name')),
('enabled', models.BooleanField(db_index=True, default=False, verbose_name='Enabled')),
(
'prefered_assertion_consumer_binding',
models.CharField(
choices=[
('meta', 'Use the default from the metadata file'),
('art', 'Artifact binding'),
('post', 'POST binding'),
],
default='meta',
max_length=4,
verbose_name='Prefered assertion consumer binding',
),
),
('encrypt_nameid', models.BooleanField(default=False, verbose_name='Encrypt NameID')),
('encrypt_assertion', models.BooleanField(default=False, verbose_name='Encrypt Assertion')),
(
'authn_request_signed',
models.BooleanField(default=False, verbose_name='Authentication request signed'),
),
(
'idp_initiated_sso',
models.BooleanField(db_index=True, default=False, verbose_name='Allow IdP initiated SSO'),
),
(
'default_name_id_format',
models.CharField(
choices=[
('none', 'None'),
('persistent', 'Persistent'),
('transient', 'Transient'),
('email', 'Email'),
('username', 'Username (use with Google Apps)'),
('uuid', 'UUID'),
('edupersontargetedid', 'Use eduPersonTargetedID attribute'),
],
default='none',
max_length=256,
),
),
(
'accepted_name_id_format',
authentic2.saml.fields.MultiSelectField(
blank=True,
choices=[
('none', 'None'),
('persistent', 'Persistent'),
('transient', 'Transient'),
('email', 'Email'),
('username', 'Username (use with Google Apps)'),
('uuid', 'UUID'),
('edupersontargetedid', 'Use eduPersonTargetedID attribute'),
],
max_length=1024,
verbose_name='NameID formats accepted',
),
),
(
'ask_user_consent',
models.BooleanField(
default=False, verbose_name='Ask user for consent when creating a federation'
),
),
(
'accept_slo',
models.BooleanField(
db_index=True, default=True, verbose_name='Accept to receive Single Logout requests'
),
),
(
'forward_slo',
models.BooleanField(default=True, verbose_name='Forward Single Logout requests'),
),
(
'needs_iframe_logout',
models.BooleanField(
default=False,
help_text='logout URL are normally loaded inside an <img> HTML tag, some service provider need to use an iframe',
verbose_name='needs iframe logout',
),
),
(
'iframe_logout_timeout',
models.PositiveIntegerField(
default=300,
help_text="if iframe logout is used, it's the time between the onload event for this iframe and the moment we "
'consider its loading to be really finished',
verbose_name='iframe logout timeout',
),
),
(
'http_method_for_slo_request',
models.IntegerField(
choices=[(4, 'Redirect binding'), (5, 'SOAP binding')],
default=4,
verbose_name='HTTP binding for the SLO requests',
),
),
(
'federation_mode',
models.PositiveIntegerField(
choices=[(0, 'explicit'), (1, 'implicit')], default=0, verbose_name='federation mode'
),
),
],
options={
'verbose_name': 'service provider options policy',
'verbose_name_plural': 'service provider options policies',
},
),
migrations.CreateModel(
name='LibertyServiceProvider',
fields=[
(
'liberty_provider',
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
primary_key=True,
related_name='service_provider',
serialize=False,
to='saml.libertyprovider',
),
),
('enabled', models.BooleanField(db_index=True, default=False, verbose_name='Enabled')),
(
'enable_following_sp_options_policy',
models.BooleanField(
default=False,
verbose_name='The following options policy will apply except if a policy for all service provider is defined.',
),
),
(
'users_can_manage_federations',
models.BooleanField(
blank=True, db_index=True, default=True, verbose_name='users can manage federation'
),
),
],
options={
'verbose_name': 'SAML service provider',
'verbose_name_plural': 'SAML service providers',
},
),
migrations.CreateModel(
name='LibertySessionDump',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('django_session_key', models.CharField(max_length=128)),
('session_dump', models.TextField(blank=True)),
('kind', models.IntegerField(choices=[(0, 'sp'), (1, 'idp')])),
],
options={
'verbose_name': 'SAML session dump',
'verbose_name_plural': 'SAML session dumps',
'unique_together': {('django_session_key', 'kind')},
},
),
migrations.CreateModel(
name='LibertySession',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('django_session_key', models.CharField(max_length=128)),
('session_index', models.CharField(max_length=80)),
('provider_id', models.CharField(max_length=256)),
('name_id_qualifier', models.CharField(max_length=256, null=True, verbose_name='Qualifier')),
('name_id_format', models.CharField(max_length=100, null=True, verbose_name='NameIDFormat')),
('name_id_content', models.CharField(max_length=100, verbose_name='NameID')),
(
'name_id_sp_name_qualifier',
models.CharField(max_length=256, null=True, verbose_name='SPNameQualifier'),
),
('creation', models.DateTimeField(auto_now_add=True)),
(
'federation',
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to='saml.libertyfederation',
),
),
],
options={
'verbose_name': 'SAML session',
'verbose_name_plural': 'SAML sessions',
},
),
migrations.CreateModel(
name='SAMLAttribute',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('object_id', models.PositiveIntegerField(verbose_name='object identifier')),
(
'name_format',
models.CharField(
choices=[('basic', 'Basic'), ('uri', 'URI'), ('unspecified', 'Unspecified')],
default='basic',
max_length=64,
verbose_name='name format',
),
),
(
'name',
models.CharField(
blank=True,
help_text='the local attribute name is used if left blank',
max_length=128,
verbose_name='name',
),
),
('friendly_name', models.CharField(blank=True, max_length=64, verbose_name='friendly name')),
('attribute_name', models.CharField(max_length=64, verbose_name='attribute name')),
('enabled', models.BooleanField(blank=True, default=True, verbose_name='enabled')),
(
'content_type',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to='contenttypes.contenttype',
verbose_name='content type',
),
),
],
options={
'unique_together': {
('content_type', 'object_id', 'name_format', 'name', 'friendly_name', 'attribute_name')
},
},
),
migrations.AddIndex(
model_name='libertysession',
index=models.Index(
fields=['provider_id', 'django_session_key'], name='saml_libert_provide_39bb6c_idx'
),
),
migrations.AddField(
model_name='libertyserviceprovider',
name='sp_options_policy',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name='sp_options_policy',
to='saml.spoptionsidppolicy',
verbose_name='service provider options policy',
),
),
migrations.AddField(
model_name='libertyfederation',
name='sp',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to='saml.libertyserviceprovider',
),
),
]

Some files were not shown because too many files have changed in this diff Show More