nettoyage des applications obsolètes/squash des migrations (#40685) (série partielle) #262

Open
bdauvergne wants to merge 9 commits from wip/40685-squash-des-migrations-partial into main
106 changed files with 190 additions and 3119 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'
pmarillonnet marked this conversation as resolved Outdated

Par curiosité, qu’est-ce ce qui se passe mal dans check-migrations si on ne pose pas ça ?

Par curiosité, qu’est-ce ce qui se passe mal dans `check-migrations` si on ne pose pas ça ?

Si les locales sont compilées, on se retrouve avec erreurs de migrations manquante parce que verbose_name/help_text ne correspondent plus.

Si les locales sont compilées, on se retrouve avec erreurs de migrations manquante parce que verbose_name/help_text ne correspondent plus.

Ok

Ok
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

@ -9,7 +9,7 @@ class Migration(migrations.Migration):
dependencies = [
('authentic2', '0004_service'),
('django_rbac', '__first__'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('custom_user', '__first__'),
('contenttypes', '0001_initial'),
]
@ -118,7 +118,7 @@ class Migration(migrations.Migration):
),
(
'members',
models.ManyToManyField(related_name='roles', to=settings.AUTH_USER_MODEL, blank=True),
models.ManyToManyField(related_name='roles', to='custom_user.User', blank=True),
),
(
'ou',

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

@ -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
@ -13,7 +12,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('custom_user', '__first__'),
('authentic2', '0027_remove_deleteduser'),
('sessions', '0001_initial'),
]
@ -92,7 +91,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

@ -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',
pmarillonnet marked this conversation as resolved Outdated

Ok, j’ignorais que auth_migrations_18 avait défini autant de modèles à un moment. Dégageons tout cela oui.

Ok, j’ignorais que `auth_migrations_18` avait défini autant de modèles à un moment. Dégageons tout cela oui.
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
@ -40,17 +39,13 @@ class Migration(migrations.Migration):
('admin', '__first__'),
]
run_before = [
('auth', '0003_auto_20150410_1657'),
]
operations = [
# Django admin log
ThirdPartyAlterField(
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

@ -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

@ -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',
pmarillonnet marked this conversation as resolved Outdated

On pourrait en profiter pour dégager ce gabarit aussi.

On pourrait en profiter pour dégager ce gabarit aussi.

Bonne idée.

Bonne idée.
{
'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 BadRequest, 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,8 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0002_auto_20150323_1720'),
('auth', '__first__'),
('custom_user', '__first__'),
('contenttypes', '0001_initial'),
]
@ -98,7 +99,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 +187,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 +209,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,9 @@
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('custom_user', '__first__'),
('authentic2', '0002_auto_20150320_1418'),
]
@ -12,25 +11,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

@ -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

@ -1,13 +1,12 @@
# 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),
('custom_user', '__first__'),
('authentic2', '0047_initialize_services_runtime_settings'),
]
@ -23,7 +22,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

@ -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,7 @@ import authentic2.saml.models
class Migration(migrations.Migration):
dependencies = [
('auth', '__first__'),
('idp', '__first__'),
('custom_user', '__first__'),
('contenttypes', '0001_initial'),
]
@ -474,17 +473,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 +745,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,10 @@
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),
('custom_user', '__first__'),
('saml', '0003_merge'),
]
@ -16,7 +15,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

@ -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

@ -554,7 +554,7 @@ class LibertyFederation(models.Model):
"""Store a federation, i.e. an identifier shared with another provider, be
it IdP or SP"""
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL)
user = models.ForeignKey('custom_user.User', null=True, blank=True, on_delete=models.SET_NULL)
sp = models.ForeignKey('LibertyServiceProvider', null=True, blank=True, on_delete=models.CASCADE)
name_id_format = models.CharField(max_length=100, verbose_name='NameIDFormat', blank=True, null=True)
name_id_content = models.CharField(max_length=100, verbose_name='NameID')

View File

@ -153,9 +153,7 @@ INSTALLED_APPS = (
'authentic2_idp_oidc',
'authentic2.nonce',
pmarillonnet marked this conversation as resolved
Review

Est-ce qu’on pourrait pas aussi dégager cette appli de la déclaration des applis installées ?

Est-ce qu’on pourrait pas aussi dégager cette appli de la déclaration des applis installées ?
Review

Pas encore sinon la migration delete_nonce ne s'appliquera pas et on aura une table qui traîne.

Pas encore sinon la migration delete_nonce ne s'appliquera pas et on aura une table qui traîne.
Review

Ah oui ok, bien vu.

Ah oui ok, bien vu.
'authentic2.saml',
'authentic2.idp',
'authentic2.idp.saml',
'authentic2.attribute_aggregator',
'authentic2.disco_service',
'authentic2.manager',
'authentic2.apps.authenticators',
@ -312,10 +310,6 @@ LOGGING = {
},
}
MIGRATION_MODULES = {
'auth': 'authentic2.auth_migrations_18',
}
# Django REST Framework
REST_FRAMEWORK = {
'NON_FIELD_ERRORS_KEY': '__all__',

View File

@ -1,39 +0,0 @@
{% extends "authentic2/base-page.html" %}
{% load i18n %}
{% block title %} {% trans "Consent page for attribute propagation" %} {% endblock %}
{% block content %}
{% load i18n %}
<div id="consent">
<form method="post">
{% csrf_token %}
<p>
{% trans "Choose attributes to send to " %} <strong>{{ provider_id }}</strong> ?
<ul>
{% for i, name, values in attributes %}
<li>
{% if allow_selection %}
<input type="checkbox" name="attribute_nb_{{ i }}" value="{{ i }}" checked="checked"/>
{% endif %}
{{ name }} : <strong>{% for v in values %}{{ v }} {% endfor %}</strong></li>
{% endfor %}
</ul>
</p>
<input type="hidden" name="next" value="{{ next }}" />
<input type="hidden" name="nonce" value="{{ nonce }}" />
{% if allow_selection %}
<button name="accept">{% trans 'Send selected' %}</button>
{% else %}
<button name="accept">{% trans 'Send all' %}</button>
{% endif %}
<button name="refuse">{% trans 'Refuse all' %}</button>
</form>
</div>
{% endblock %}

View File

@ -1,32 +0,0 @@
{% extends "authentic2/base-page.html" %}
{% load i18n %}
{% block title %} {% trans "Consent page for federation" %} {% endblock %}
{% block content %}
{% load i18n %}
<div id="consent">
<p>
{% if provider_id %}
{% blocktrans trimmed with provider_id=provider_id %}
Do you accept to federate your account with <strong>{{ provider_id }}</strong>?
{% endblocktrans %}
{% else %}
{% trans "Do you accept to federate your account?" %}
{% endif %}
{{ provider_id2 }}
</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />
<input type="hidden" name="nonce" value="{{ nonce }}" />
<div class="buttons">
<button name="accept">{% trans 'Accept' %}</button>
<button name="refuse">{% trans 'Refuse' %}</button>
</div>
</form>
</div>
{% endblock %}

View File

@ -106,7 +106,6 @@ urlpatterns = [
re_path(r'^su/(?P<uuid>[A-Za-z0-9_-]+)/$', views.su, name='su'),
path('accounts/', include(accounts_urlpatterns)),
re_path(r'^admin/', admin.site.urls),
path('idp/', include('authentic2.idp.urls')),
path('manage/', include('authentic2.manager.urls')),
path('api/', include('authentic2.api_urls')),
path('continue/', views.display_message_and_continue, name='continue'),

View File

@ -31,7 +31,6 @@ from django import forms
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth import authenticate as dj_authenticate
from django.contrib.auth import get_user_model
from django.contrib.auth import login as auth_login
from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured, ValidationError
from django.core.mail import EmailMessage, send_mail
@ -808,8 +807,9 @@ def send_registration_mail(request, email, ou, template_names=None, next_url=Non
Can raise an smtplib.SMTPException
"""
from authentic2.custom_user.models import User
logger = logging.getLogger(__name__)
User = get_user_model()
if not template_names:
template_names = ['authentic2/activation_email']
@ -1197,6 +1197,8 @@ def get_manager_login_url():
def send_email_change_email(user, email, request=None, context=None, template_names=None):
'''Send an email to verify that user can take email as its new email'''
from authentic2.custom_user.models import User
assert user
assert email
@ -1222,7 +1224,7 @@ def send_email_change_email(user, email, request=None, context=None, template_na
# check if email should be unique and is not
email_is_not_unique = False
qs = get_user_model().objects.all()
qs = User.objects.all()
if app_settings.A2_EMAIL_IS_UNIQUE:
email_is_not_unique = qs.filter(email=email).exclude(pk=user.pk).exists()
elif user.ou and user.ou.email_is_unique:

View File

@ -29,8 +29,9 @@ from django.utils.translation import gettext_lazy as _
from . import app_settings
# copied from http://www.djangotips.com/real-email-validation
@deconstructible
pmarillonnet marked this conversation as resolved Outdated

J’ai pas capté ce qui justifiait d’ajouter ce décorateur là maintenant, dans cette PR :/

J’ai pas capté ce qui justifiait d’ajouter ce décorateur là maintenant, dans cette PR :/

C'est la recréation de la migration pour le modèle User qui utiliser EmailValidator et sans le décorateur deconstructible make_migration n'accepter pas de la refaire, problème invisible avant (et je suppose que l'introduction de EmailValidator s'est faite en monkeypatchant les migrations existantes donc on l'a pas vu non plus au passage).

Donc c'est utile, mais plus loin dans l'autre série de commits.

C'est la recréation de la migration pour le modèle User qui utiliser EmailValidator et sans le décorateur deconstructible make_migration n'accepter pas de la refaire, problème invisible avant (et je suppose que l'introduction de EmailValidator s'est faite en monkeypatchant les migrations existantes donc on l'a pas vu non plus au passage). Donc c'est utile, mais plus loin dans l'autre série de commits.

Ok.

Ok.
class EmailValidator:
# copied from http://www.djangotips.com/real-email-validation
def __init__(self, rcpt_check=False):
self.rcpt_check = rcpt_check

View File

@ -23,7 +23,7 @@ from email.utils import parseaddr
from django import shortcuts
from django.conf import settings
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.contrib.auth import logout as auth_logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import PasswordChangeView as DjPasswordChangeView
@ -49,7 +49,7 @@ from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateVi
from ratelimit.utils import is_ratelimited
from authentic2.a2_rbac.models import Role
from authentic2.custom_user.models import iter_attributes
from authentic2.custom_user.models import User, iter_attributes
from authentic2.forms import authentication as authentication_forms
from authentic2_idp_oidc.models import OIDCAuthorization
@ -70,8 +70,6 @@ from .utils.sms import SMSError, send_registration_sms, sms_ratelimit_key
from .utils.view_decorators import enable_view_restriction
from .utils.views import csrf_token_check
User = get_user_model()
logger = logging.getLogger(__name__)
@ -411,7 +409,7 @@ class PhoneChangeVerifyView(TemplateView):
def get(self, request, *args, **kwargs):
token = kwargs['token']
authn = utils_misc.get_password_authenticator()
user_ct = ContentType.objects.get_for_model(get_user_model())
user_ct = ContentType.objects.get_for_model(User)
try:
token = models.Token.objects.get(
uuid=token,
@ -1687,7 +1685,7 @@ class RegistrationView(cbv.ValidateCSRFMixin, BaseRegistrationView):
class RegistrationCompletionView(CreateView):
model = get_user_model()
model = User
success_url = 'auth_homepage'
def get_template_names(self):
@ -1722,7 +1720,7 @@ class RegistrationCompletionView(CreateView):
def dispatch(self, request, *args, **kwargs):
registration_token = kwargs['registration_token'].replace(' ', '')
self.authenticator = utils_misc.get_password_authenticator()
user_ct = ContentType.objects.get_for_model(get_user_model())
user_ct = ContentType.objects.get_for_model(User)
try:
token = models.Token.use('registration', registration_token, delete=False)
except models.Token.DoesNotExist:
@ -1876,7 +1874,7 @@ class RegistrationCompletionView(CreateView):
for key in keys:
if key in attributes:
init_kwargs[key] = attributes[key]
kwargs['instance'] = get_user_model()(**init_kwargs)
kwargs['instance'] = User(**init_kwargs)
# phone identifier is a separate attribute and is set post user-creation
if hasattr(self, 'phone'):
kwargs['instance'].phone_verified_on = timezone.now()
@ -1998,7 +1996,7 @@ class RegistrationCompletionView(CreateView):
if (phone := getattr(self, 'phone', None)) and self.authenticator.is_phone_authn_active:
# phone identifier set post user-creation
models.AttributeValue.objects.create(
content_type=ContentType.objects.get_for_model(get_user_model()),
content_type=ContentType.objects.get_for_model(User),
object_id=user.id,
content=phone,
attribute=self.authenticator.phone_identifier_field,
@ -2136,7 +2134,7 @@ class ValidateDeletionView(TemplateView):
)
self.prompt = deletion_token.get('prompt', self.prompt)
user_pk = deletion_token['user_pk']
self.user = get_user_model().objects.get(pk=user_pk)
self.user = User.objects.get(pk=user_pk)
# A user account wont be deactived twice
if not self.user.is_active:
raise ValidationError(_('This account is inactive, it cannot be deleted.'))
@ -2149,7 +2147,7 @@ class ValidateDeletionView(TemplateView):
error = _('The account deletion request was not on this site, try again')
except ValidationError as e:
error = e.message
except get_user_model().DoesNotExist:
except User.DoesNotExist:
error = _('This account has previously been deleted.')
if error:

View File

@ -14,13 +14,13 @@
# 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.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework.response import Response
from authentic2.api_views import DjangoPermission
from authentic2.compat.drf import action
from authentic2.custom_user.models import User
@action(
@ -30,6 +30,6 @@ from authentic2.compat.drf import action
permission_classes=(DjangoPermission('custom_user.view_user'),),
)
def fc_unlink(self, request, uuid):
user = get_object_or_404(get_user_model(), uuid=uuid)
user = get_object_or_404(User, uuid=uuid)
user.fc_accounts.all().delete()
return Response(status=status.HTTP_204_NO_CONTENT)

View File

@ -16,14 +16,12 @@
import logging
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.core.exceptions import MultipleObjectsReturned, PermissionDenied
from . import models
logger = logging.getLogger(__name__)
User = get_user_model()
class FcBackend(ModelBackend):

View File

@ -1,10 +1,9 @@
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('custom_user', '__first__'),
]
operations = [
@ -23,7 +22,7 @@ class Migration(migrations.Migration):
models.ForeignKey(
related_name='fc_accounts',
verbose_name='user',
to=settings.AUTH_USER_MODEL,
to='custom_user.User',
on_delete=models.CASCADE,
),
),

View File

@ -16,7 +16,6 @@
import json
from django.conf import settings
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.utils.functional import cached_property
@ -143,7 +142,7 @@ class FcAccount(models.Model):
created = models.DateTimeField(verbose_name=_('created'), auto_now_add=True)
modified = models.DateTimeField(verbose_name=_('modified'), auto_now=True)
user = models.ForeignKey(
to=settings.AUTH_USER_MODEL,
to='custom_user.User',
verbose_name=_('user'),
related_name='fc_accounts',
on_delete=models.CASCADE,

View File

@ -20,7 +20,6 @@ import time
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.views import update_session_auth_hash
from django.core.cache import cache
from django.core.exceptions import PermissionDenied, ValidationError
@ -39,6 +38,7 @@ from requests_oauthlib import OAuth2Session
from authentic2 import app_settings as a2_app_settings
from authentic2 import constants
from authentic2.a2_rbac.utils import get_default_ou
from authentic2.custom_user.models import User
from authentic2.forms.passwords import SetPasswordForm
from authentic2.models import Attribute, AttributeValue, Lock
from authentic2.utils import hooks
@ -52,7 +52,6 @@ from . import app_settings, models, utils
from .utils import apply_user_info_mappings, build_logout_url, clean_fc_session
logger = logging.getLogger(__name__)
User = get_user_model()
class EmailExistsError(Exception):

View File

@ -20,7 +20,6 @@ import logging
import requests
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db import IntegrityError
from django.db.transaction import atomic
@ -31,6 +30,7 @@ from jwcrypto.jwt import JWT
from authentic2 import app_settings
from authentic2.a2_rbac.models import OrganizationalUnit
from authentic2.custom_user.models import User
from authentic2.models import Lock
from authentic2.utils import hooks
from authentic2.utils.crypto import base64url_encode
@ -243,7 +243,6 @@ class OIDCBackend(ModelBackend):
Lock.lock_identifier(identifier=id_token.sub)
User = get_user_model()
user = None
if provider.strategy == models.OIDCProvider.STRATEGY_FIND_UUID:
# use the OP sub to find an user by UUUID

View File

@ -7,7 +7,7 @@ import authentic2_auth_oidc.models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('custom_user', '__first__'),
migrations.swappable_dependency(settings.RBAC_OU_MODEL),
]
@ -155,7 +155,7 @@ class Migration(migrations.Migration):
field=models.OneToOneField(
related_name='oidc_account',
verbose_name='user',
to=settings.AUTH_USER_MODEL,
to='custom_user.User',
on_delete=models.CASCADE,
),
),

View File

@ -1,12 +1,11 @@
# Generated by Django 2.2.17 on 2020-11-02 10:42
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('custom_user', '__first__'),
('authentic2_auth_oidc', '0007_auto_20200317_1732'),
]

View File

@ -19,7 +19,6 @@ import logging
from datetime import datetime, timedelta
import requests
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models, transaction
from django.db.models import JSONField
@ -482,7 +481,7 @@ class OIDCAccount(models.Model):
to='OIDCProvider', verbose_name=_('provider'), related_name='accounts', on_delete=models.CASCADE
)
user = models.OneToOneField(
to=settings.AUTH_USER_MODEL,
to='custom_user.User',
verbose_name=_('user'),
related_name='oidc_account',
on_delete=models.CASCADE,

View File

@ -5,7 +5,7 @@ import authentic2_idp_cas.models
class Migration(migrations.Migration):
dependencies = [
('auth', '0002_auto_20150323_1720'),
('custom_user', '__first__'),
]
operations = [
@ -128,7 +128,7 @@ class Migration(migrations.Migration):
'user',
models.ForeignKey(
blank=True,
to='auth.User',
to='custom_user.User',
max_length=128,
null=True,
verbose_name='user',

View File

@ -1,10 +1,9 @@
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('custom_user', '__first__'),
('authentic2_idp_cas', '0001_initial'),
]
@ -14,7 +13,7 @@ class Migration(migrations.Migration):
name='user',
field=models.ForeignKey(
blank=True,
to=settings.AUTH_USER_MODEL,
to='custom_user.User',
max_length=128,
null=True,
verbose_name='user',

View File

@ -142,14 +142,14 @@ class AppConfig(django.apps.AppConfig):
data['unknown_uuids'] = new_unknown_uuids
def a2_hook_api_modify_queryset(self, view, qs):
from django.contrib.auth import get_user_model
from django.db.models import Q
from django.utils.timezone import now
from authentic2.custom_user.models import User
from .models import OIDCAuthorization, OIDCClient
client = self.get_oidc_client(view)
User = get_user_model()
# fast path
if not issubclass(qs.model, User) or client is None:

View File

@ -16,11 +16,11 @@
from django import forms
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from authentic2.attributes_ng.engine import get_service_attributes
from authentic2.custom_user.models import User
from authentic2.forms.mixins import SlugMixin
from authentic2.forms.widgets import DatalistTextInput
from authentic2.middleware import StoreRequestMiddleware
@ -63,7 +63,7 @@ class OIDCClientForm(SlugMixin, forms.ModelForm):
user = kwargs.pop('user')
super().__init__(*args, **kwargs)
# hide internal functionalities from regular administrators
if not (user and isinstance(user, get_user_model()) and user.is_superuser):
if not (user and isinstance(user, User) and user.is_superuser):
del self.fields['has_api_access']
del self.fields['activate_user_profiles']

View File

@ -1,4 +1,3 @@
from django.conf import settings
from django.db import migrations, models
import authentic2_idp_oidc.models
@ -6,7 +5,7 @@ import authentic2_idp_oidc.models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('custom_user', '__first__'),
('authentic2', '0016_attribute_disabled'),
]
@ -143,9 +142,7 @@ class Migration(migrations.Migration):
),
(
'user',
models.ForeignKey(
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
),
models.ForeignKey(verbose_name='user', to='custom_user.User', on_delete=models.CASCADE),
),
],
),
@ -159,9 +156,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='oidcauthorization',
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),
),
migrations.AddField(
model_name='oidcaccesstoken',
@ -173,8 +168,6 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='oidcaccesstoken',
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),
),
]

View File

@ -290,7 +290,7 @@ class OIDCAuthorization(models.Model):
)
client_id = models.PositiveIntegerField(verbose_name=_('client id'))
client = GenericForeignKey('client_ct', 'client_id')
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.CASCADE)
user = models.ForeignKey(to='custom_user.User', verbose_name=_('user'), on_delete=models.CASCADE)
scopes = models.TextField(blank=False, verbose_name=_('scopes'))
profile = models.ForeignKey(to=Profile, verbose_name=_('profile'), on_delete=models.CASCADE, null=True)
@ -356,7 +356,7 @@ class OIDCCode(SessionMixin, models.Model):
uuid = models.CharField(max_length=128, verbose_name=_('uuid'), default=generate_uuid)
client = models.ForeignKey(to=OIDCClient, verbose_name=_('client'), on_delete=models.CASCADE)
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.CASCADE)
user = models.ForeignKey(to='custom_user.User', verbose_name=_('user'), on_delete=models.CASCADE)
profile = models.ForeignKey(
to=Profile, verbose_name=_('user selected profile'), null=True, on_delete=models.CASCADE
)
@ -405,7 +405,7 @@ class OIDCCode(SessionMixin, models.Model):
class OIDCAccessToken(SessionMixin, models.Model):
uuid = models.CharField(max_length=128, verbose_name=_('uuid'), default=generate_uuid, db_index=True)
client = models.ForeignKey(to=OIDCClient, verbose_name=_('client'), on_delete=models.CASCADE)
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name=_('user'), on_delete=models.CASCADE)
user = models.ForeignKey(to='custom_user.User', verbose_name=_('user'), on_delete=models.CASCADE)
scopes = models.TextField(verbose_name=_('scopes'))
session_key = models.CharField(verbose_name=_('session key'), max_length=128, blank=True)
profile = models.ForeignKey(to=Profile, verbose_name=_('profile'), on_delete=models.CASCADE, null=True)

View File

@ -6,7 +6,7 @@ import authentic2.a2_rbac
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('custom_user', '__first__'),
migrations.swappable_dependency(settings.RBAC_OU_MODEL),
migrations.swappable_dependency(settings.RBAC_PERMISSION_MODEL),
migrations.swappable_dependency(settings.RBAC_ROLE_MODEL),
@ -102,7 +102,7 @@ class Migration(migrations.Migration):
('description', models.TextField(verbose_name='description', blank=True)),
(
'members',
models.ManyToManyField(related_name='roles', to=settings.AUTH_USER_MODEL, blank=True),
models.ManyToManyField(related_name='roles', to='custom_user.User', blank=True),
),
(
'ou',

View File

@ -733,129 +733,6 @@ def test_change_email(settings, app, franceconnect, mailoutbox, freezer):
assert User.objects.get().email == 'jane.doe@example.com'
def test_fc_authenticator_data_migration(migration, settings):
app = 'authentic2_auth_fc'
migrate_from = [(app, '0005_fcauthenticator')]
migrate_to = [(app, '0006_auto_20220525_1409')]
old_apps = migration.before(migrate_from)
FcAuthenticator = old_apps.get_model(app, 'FcAuthenticator')
settings.AUTH_FRONTENDS_KWARGS = {
'fc': {'priority': 3, 'show_condition': "'backoffice' not in login_hint"}
}
settings.A2_FC_ENABLE = True
settings.A2_FC_CLIENT_ID = '211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6k'
settings.A2_FC_CLIENT_SECRET = '211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6z'
settings.A2_FC_AUTHORIZE_URL = 'https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize'
settings.A2_FC_SCOPES = ['profile', 'email', 'birthdate']
new_apps = migration.apply(migrate_to)
FcAuthenticator = new_apps.get_model(app, 'FcAuthenticator')
authenticator = FcAuthenticator.objects.get()
assert authenticator.slug == 'fc-authenticator'
assert authenticator.order == 3
assert authenticator.show_condition == "'backoffice' not in login_hint"
assert authenticator.enabled is True
assert authenticator.platform == 'test'
assert authenticator.client_id == '211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6k'
assert authenticator.client_secret == '211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6z'
assert authenticator.scopes == ['profile', 'email', 'birthdate']
# 0007 should have no effect
new_apps = migration.apply([(app, '0007_auto_20220615_1002')])
FcAuthenticator = new_apps.get_model(app, 'FcAuthenticator')
assert FcAuthenticator.objects.get().pk == authenticator.pk
def test_fc_authenticator_data_migration_defaults(migration, settings):
app = 'authentic2_auth_fc'
migrate_from = [(app, '0005_fcauthenticator')]
migrate_to = [(app, '0006_auto_20220525_1409')]
old_apps = migration.before(migrate_from)
FcAuthenticator = old_apps.get_model(app, 'FcAuthenticator')
settings.A2_FC_ENABLE = False
new_apps = migration.apply(migrate_to)
FcAuthenticator = new_apps.get_model(app, 'FcAuthenticator')
authenticator = FcAuthenticator.objects.get()
assert authenticator.slug == 'fc-authenticator'
assert authenticator.order == -1
assert authenticator.show_condition == ''
assert authenticator.enabled is False
assert authenticator.platform == 'test'
assert authenticator.client_id == ''
assert authenticator.client_secret == ''
assert authenticator.scopes == ['profile', 'email']
def test_fc_authenticator_data_migration_empty_configuration(migration, settings):
app = 'authentic2_auth_fc'
migrate_from = [(app, '0005_fcauthenticator')]
migrate_to = [(app, '0006_auto_20220525_1409')]
old_apps = migration.before(migrate_from)
FcAuthenticator = old_apps.get_model(app, 'FcAuthenticator')
new_apps = migration.apply(migrate_to)
FcAuthenticator = new_apps.get_model(app, 'FcAuthenticator')
assert not FcAuthenticator.objects.exists()
def test_fc_authenticator_data_migration_bad_settings(migration, settings):
app = 'authentic2_auth_fc'
migrate_from = [(app, '0005_fcauthenticator')]
migrate_to = [(app, '0006_auto_20220525_1409')]
old_apps = migration.before(migrate_from)
FcAuthenticator = old_apps.get_model(app, 'FcAuthenticator')
settings.AUTH_FRONTENDS_KWARGS = {'fc': {'priority': None, 'show_condition': None}}
settings.A2_FC_ENABLE = False
settings.A2_FC_CLIENT_ID = 'x' * 260
settings.A2_FC_CLIENT_SECRET = None
settings.A2_FC_AUTHORIZE_URL = 'https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize'
settings.A2_FC_SCOPES = None
new_apps = migration.apply(migrate_to)
FcAuthenticator = new_apps.get_model(app, 'FcAuthenticator')
authenticator = FcAuthenticator.objects.get()
assert authenticator.slug == 'fc-authenticator'
assert authenticator.order == -1
assert authenticator.show_condition == ''
assert authenticator.enabled is False
assert authenticator.platform == 'test'
assert authenticator.client_id == 'x' * 256
assert authenticator.client_secret == ''
assert authenticator.scopes == ['profile', 'email']
def test_fc_authenticator_data_migration_fixup(migration, settings):
app = 'authentic2_auth_fc'
migrate_from = [(app, '0006_auto_20220525_1409')]
migrate_to = [(app, '0007_auto_20220615_1002')]
old_apps = migration.before(migrate_from)
FcAuthenticator = old_apps.get_model(app, 'FcAuthenticator')
# authenticator was not created by 0006
assert not FcAuthenticator.objects.exists()
settings.A2_FC_ENABLE = True
settings.A2_FC_CLIENT_ID = '211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6k'
settings.A2_FC_CLIENT_SECRET = '211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6z'
new_apps = migration.apply(migrate_to)
FcAuthenticator = new_apps.get_model(app, 'FcAuthenticator')
# authenticator was created by 0007
authenticator = FcAuthenticator.objects.get()
assert authenticator.slug == 'fc-authenticator'
assert authenticator.client_id == '211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6k'
def test_bad_email_handling(settings, app, franceconnect, caplog):
caplog.set_level(logging.WARNING)

View File

@ -1,43 +0,0 @@
# authentic2 - authentic2 authentication for FranceConnect
# Copyright (C) 2020 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/>.
def test_migration_0003_0004_fcaccount_order(migration):
migrate_from = [('authentic2_auth_fc', '0002_auto_20200416_1439')]
# it's a two-parts migration, as it contains data and schema changes.
migrate_to = [('authentic2_auth_fc', '0004_fcaccount_order2')]
old_apps = migration.before(migrate_from, at_end=False)
User = old_apps.get_model('custom_user', 'User')
FcAccount = old_apps.get_model('authentic2_auth_fc', 'FcAccount')
user1 = User.objects.create(username='user1')
user2 = User.objects.create(username='user2')
user3 = User.objects.create(username='user3')
FcAccount.objects.create(user=user1, sub='sub1')
FcAccount.objects.create(user=user1, sub='sub2')
FcAccount.objects.create(user=user2, sub='sub2')
FcAccount.objects.create(user=user2, sub='sub3')
FcAccount.objects.create(user=user3, sub='sub3')
assert len(set(FcAccount.objects.values_list('user_id', flat=True))) == 3
assert len(set(FcAccount.objects.values_list('sub', flat=True))) == 3
assert FcAccount.objects.count() == 5
# execute migration
new_apps = migration.apply(migrate_to)
FcAccount = new_apps.get_model('authentic2_auth_fc', 'FcAccount')
assert len(set(FcAccount.objects.values_list('user_id', 'order'))) == 5
assert len(set(FcAccount.objects.values_list('sub', 'order'))) == 5

View File

@ -1308,78 +1308,6 @@ def test_auth_time_is_null(app, caplog, code, oidc_provider, oidc_provider_jwkse
assert User.objects.count() == 1
@pytest.mark.parametrize(
'auth_frontend_kwargs',
[
{'oidc': {'priority': 3, 'show_condition': '"backoffice" not in login_hint'}},
{'oidc': {'show_condition': {'baz': '"backoffice" not in login_hint', 'bar': 'True'}}},
],
)
def test_oidc_provider_authenticator_data_migration(auth_frontend_kwargs, migration, settings):
settings.AUTH_FRONTENDS_KWARGS = auth_frontend_kwargs
app = 'authentic2_auth_oidc'
migrate_from = [(app, '0008_auto_20201102_1142')]
migrate_to = [(app, '0012_auto_20220524_1147')]
old_apps = migration.before(migrate_from)
OIDCProvider = old_apps.get_model(app, 'OIDCProvider')
OIDCClaimMapping = old_apps.get_model(app, 'OIDCClaimMapping')
OIDCAccount = old_apps.get_model(app, 'OIDCAccount')
OrganizationalUnit = old_apps.get_model('a2_rbac', 'OrganizationalUnit')
User = old_apps.get_model('custom_user', 'User')
ou1 = OrganizationalUnit.objects.create(name='OU1', slug='ou1')
issuer = 'https://baz.example.com'
first_provider = OIDCProvider.objects.create(
name='Baz',
slug='baz',
ou=ou1,
show=True,
issuer=issuer,
authorization_endpoint='%s/authorize' % issuer,
token_endpoint='%s/token' % issuer,
end_session_endpoint='%s/logout' % issuer,
userinfo_endpoint='%s/user_info' % issuer,
token_revocation_endpoint='%s/revoke' % issuer,
)
second_provider = OIDCProvider.objects.create(name='Second', slug='second', ou=ou1)
second_provider_claim_mapping = OIDCClaimMapping.objects.create(
provider=second_provider, claim='second_provider', attribute='username'
)
user1 = User.objects.create()
second_provider_account = OIDCAccount.objects.create(
user=user1, provider=second_provider, sub='second_provider'
)
first_provider_claim_mapping = OIDCClaimMapping.objects.create(
provider=first_provider, claim='first_provider', attribute='username'
)
new_apps = migration.apply(migrate_to)
OIDCProvider = new_apps.get_model(app, 'OIDCProvider')
BaseAuthenticator = new_apps.get_model('authenticators', 'BaseAuthenticator')
authenticator = OIDCProvider.objects.get(slug='baz')
assert authenticator.name == 'Baz'
assert authenticator.ou.pk == ou1.pk
assert authenticator.enabled is True
assert authenticator.order == auth_frontend_kwargs['oidc'].get('priority', 2)
assert authenticator.show_condition == '"backoffice" not in login_hint'
assert authenticator.authorization_endpoint == '%s/authorize' % issuer
assert authenticator.claim_mappings.count() == 1
assert authenticator.claim_mappings.get().pk == first_provider_claim_mapping.pk
assert not authenticator.accounts.exists()
base_authenticator = BaseAuthenticator.objects.get(slug='baz')
assert authenticator.uuid == base_authenticator.uuid
second_authenticator = OIDCProvider.objects.get(slug='second')
assert second_authenticator.name == 'Second'
assert second_authenticator.claim_mappings.count() == 1
assert second_authenticator.claim_mappings.get().pk == second_provider_claim_mapping.pk
assert second_authenticator.accounts.count() == 1
assert second_authenticator.accounts.get().pk == second_provider_account.pk
def test_only_idtoken_claims(app, caplog, code, oidc_provider, oidc_provider_jwkset):
oidc_provider.claim_mappings.update(idtoken_claim=True)
response = app.get('/').maybe_follow()

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