codestyle: add black, isort, pyupgrade (#61513)
This commit is contained in:
parent
4925ee54ae
commit
6006c96557
34
setup.py
34
setup.py
|
@ -1,13 +1,13 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools.command.install_lib import install_lib as _install_lib
|
||||
import sys
|
||||
from distutils.cmd import Command
|
||||
from distutils.command.build import build as _build
|
||||
from distutils.command.sdist import sdist
|
||||
from distutils.cmd import Command
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
from setuptools.command.install_lib import install_lib as _install_lib
|
||||
|
||||
|
||||
class compile_translations(Command):
|
||||
|
@ -23,6 +23,7 @@ class compile_translations(Command):
|
|||
def run(self):
|
||||
try:
|
||||
from django.core.management import call_command
|
||||
|
||||
for path, dirs, files in os.walk('src'):
|
||||
if 'locale' not in dirs:
|
||||
continue
|
||||
|
@ -39,7 +40,6 @@ class build(_build):
|
|||
|
||||
|
||||
class eo_sdist(sdist):
|
||||
|
||||
def run(self):
|
||||
print("creating VERSION file")
|
||||
if os.path.exists('VERSION'):
|
||||
|
@ -62,27 +62,28 @@ class install_lib(_install_lib):
|
|||
|
||||
def get_version():
|
||||
'''Use the VERSION, if absent generates a version with git describe, if not
|
||||
tag exists, take 0.0- and add the length of the commit log.
|
||||
tag exists, take 0.0- and add the length of the commit log.
|
||||
'''
|
||||
if os.path.exists('VERSION'):
|
||||
with open('VERSION', 'r') as v:
|
||||
with open('VERSION') as v:
|
||||
return v.read()
|
||||
if os.path.exists('.git'):
|
||||
p = subprocess.Popen(['git','describe','--dirty=.dirty','--match=v*'],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
p = subprocess.Popen(
|
||||
['git', 'describe', '--dirty=.dirty', '--match=v*'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
result = p.communicate()[0]
|
||||
if p.returncode == 0:
|
||||
result = result.decode('ascii').strip()[1:] # strip spaces/newlines and initial v
|
||||
if '-' in result: # not a tagged version
|
||||
result = result.decode('ascii').strip()[1:] # strip spaces/newlines and initial v
|
||||
if '-' in result: # not a tagged version
|
||||
real_number, commit_count, commit_hash = result.split('-', 2)
|
||||
version = '%s.post%s+%s' % (real_number, commit_count, commit_hash)
|
||||
else:
|
||||
version = result
|
||||
return version
|
||||
else:
|
||||
return '0.0.post%s' % len(
|
||||
subprocess.check_output(
|
||||
['git', 'rev-list', 'HEAD']).splitlines())
|
||||
return '0.0.post%s' % len(subprocess.check_output(['git', 'rev-list', 'HEAD']).splitlines())
|
||||
return '0.0'
|
||||
|
||||
|
||||
|
@ -118,6 +119,7 @@ setup(
|
|||
'build': build,
|
||||
'install_lib': install_lib,
|
||||
'compile_translations': compile_translations,
|
||||
'sdist': eo_sdist},
|
||||
'sdist': eo_sdist,
|
||||
},
|
||||
zip_safe=False,
|
||||
)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# authentic2_cut - Authentic2 plugin for CUT
|
||||
# Copyright (C) 2016 Entr'ouvert
|
||||
|
@ -19,9 +18,10 @@
|
|||
default_app_config = 'authentic2_cut.apps.AppConfig'
|
||||
|
||||
|
||||
class Plugin(object):
|
||||
class Plugin:
|
||||
def get_before_urls(self):
|
||||
from . import urls
|
||||
|
||||
return urls.urlpatterns
|
||||
|
||||
def get_apps(self):
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# authentic2_cut - Authentic2 plugin for CUT
|
||||
# Copyright (C) 2016 Entr'ouvert
|
||||
|
@ -19,10 +18,10 @@
|
|||
from . import models
|
||||
|
||||
|
||||
class RemoveFranceConnect(object):
|
||||
class RemoveFranceConnect:
|
||||
name = 'remove-franceconnect'
|
||||
title = u'Supprimer la liaison FranceConnect'
|
||||
confirm = u'Êtes-vous sûr?'
|
||||
title = 'Supprimer la liaison FranceConnect'
|
||||
confirm = 'Êtes-vous sûr?'
|
||||
permission = 'custom_user.cut_fc_user'
|
||||
|
||||
def do(self, view, request, user, *args, **kwargs):
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# authentic2_cut - Authentic2 plugin for CUT
|
||||
# Copyright (C) 2017 Entr'ouvert
|
||||
#
|
||||
|
@ -18,22 +17,18 @@
|
|||
import base64
|
||||
import uuid
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db.transaction import atomic
|
||||
from django.conf import settings
|
||||
from django.db import IntegrityError
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from rest_framework.generics import ListAPIView
|
||||
from rest_framework import serializers, pagination
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
|
||||
from django_filters.rest_framework import FilterSet
|
||||
|
||||
from authentic2_idp_oidc.utils import make_pairwise_reversible_sub
|
||||
from authentic2 import api_views
|
||||
from authentic2_idp_oidc.utils import make_pairwise_reversible_sub
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db import IntegrityError
|
||||
from django.db.transaction import atomic
|
||||
from django_filters.rest_framework import FilterSet
|
||||
from rest_framework import pagination, serializers, status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.generics import ListAPIView
|
||||
from rest_framework.response import Response
|
||||
|
||||
from . import models, utils
|
||||
|
||||
|
@ -49,7 +44,7 @@ class Base64ImageField(serializers.FileField):
|
|||
raise ValidationError('invalid base64')
|
||||
identifier = uuid.uuid4()
|
||||
data = ContentFile(content, name=identifier.urn[9:] + '.jpg')
|
||||
return super(Base64ImageField, self).to_internal_value(data)
|
||||
return super().to_internal_value(data)
|
||||
|
||||
|
||||
class CUTValidateAttachmentSerializer(serializers.Serializer):
|
||||
|
@ -57,9 +52,7 @@ class CUTValidateAttachmentSerializer(serializers.Serializer):
|
|||
|
||||
|
||||
class CUTCreateValidateSerializer(serializers.Serializer):
|
||||
justificatifs = serializers.ListField(
|
||||
child=CUTValidateAttachmentSerializer(),
|
||||
allow_empty=False)
|
||||
justificatifs = serializers.ListField(child=CUTValidateAttachmentSerializer(), allow_empty=False)
|
||||
external_id = serializers.CharField()
|
||||
|
||||
|
||||
|
@ -90,15 +83,23 @@ class CUTValidateSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
def _get_cut_validation_accepted_mime_types():
|
||||
return getattr(settings, 'CUT_VALIDATION_ACCEPTED_MIME_TYPES', [
|
||||
'image/jpeg',
|
||||
'application/pdf',
|
||||
'image/png',
|
||||
])
|
||||
return getattr(
|
||||
settings,
|
||||
'CUT_VALIDATION_ACCEPTED_MIME_TYPES',
|
||||
[
|
||||
'image/jpeg',
|
||||
'application/pdf',
|
||||
'image/png',
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@action(detail=True, methods=['get', 'post'], url_path='validate',
|
||||
permission_classes=(api_views.DjangoPermission('custom_user.cut_validate_user'),))
|
||||
@action(
|
||||
detail=True,
|
||||
methods=['get', 'post'],
|
||||
url_path='validate',
|
||||
permission_classes=(api_views.DjangoPermission('custom_user.cut_validate_user'),),
|
||||
)
|
||||
def validate_cut(self, request, uuid):
|
||||
user = self.get_object()
|
||||
origin = get_origin(request)
|
||||
|
@ -106,12 +107,14 @@ def validate_cut(self, request, uuid):
|
|||
if request.method == 'GET':
|
||||
qs = models.ValidationRequest.objects.for_origin(origin).filter(user=user).select_related('user')
|
||||
serializer = CUTValidateSerializer(qs, many=True, context={'request': request})
|
||||
return Response({
|
||||
'result': 1,
|
||||
'next': None,
|
||||
'previous': None,
|
||||
'results': serializer.data,
|
||||
})
|
||||
return Response(
|
||||
{
|
||||
'result': 1,
|
||||
'next': None,
|
||||
'previous': None,
|
||||
'results': serializer.data,
|
||||
}
|
||||
)
|
||||
serializer = CUTCreateValidateSerializer(data=request.data)
|
||||
max_size = getattr(settings, 'CUT_VALIDATION_ATTACHMENT_MAX_SIZE', 300 * 1024 * 1024)
|
||||
if serializer.is_valid():
|
||||
|
@ -121,48 +124,57 @@ def validate_cut(self, request, uuid):
|
|||
for i, attachment in enumerate(attachments):
|
||||
content = attachment['b64_content']
|
||||
if content.size > max_size:
|
||||
errors.append({
|
||||
'code': 'justificatifs-too-big',
|
||||
'page': i,
|
||||
'max-size': max_size,
|
||||
})
|
||||
errors.append(
|
||||
{
|
||||
'code': 'justificatifs-too-big',
|
||||
'page': i,
|
||||
'max-size': max_size,
|
||||
}
|
||||
)
|
||||
accepted_mime_types = _get_cut_validation_accepted_mime_types()
|
||||
mime_type = utils.mime_type_from_buffer(content.read(10000))
|
||||
if mime_type not in accepted_mime_types:
|
||||
errors.append({
|
||||
'code': 'justificatifs-bad-format',
|
||||
'page': i,
|
||||
'accepted': accepted_mime_types,
|
||||
})
|
||||
errors.append(
|
||||
{
|
||||
'code': 'justificatifs-bad-format',
|
||||
'page': i,
|
||||
'accepted': accepted_mime_types,
|
||||
}
|
||||
)
|
||||
content.name = content.name.rsplit('.', 1)[0] + '.' + mime_type.rsplit('/', 1)[1]
|
||||
# rewind cursor
|
||||
content.seek(0)
|
||||
if models.ValidationRequest.objects.filter(
|
||||
user=user, external_id=external_id).exists():
|
||||
errors.append({
|
||||
'code': 'already-exists',
|
||||
})
|
||||
if models.ValidationRequest.objects.filter(user=user, external_id=external_id).exists():
|
||||
errors.append(
|
||||
{
|
||||
'code': 'already-exists',
|
||||
}
|
||||
)
|
||||
if errors:
|
||||
response = {'result': 0, 'errors': errors}
|
||||
return Response(response, status.HTTP_400_BAD_REQUEST)
|
||||
with atomic():
|
||||
try:
|
||||
validation_request = models.ValidationRequest.objects.create(
|
||||
user=user, external_id=external_id, origin=origin)
|
||||
user=user, external_id=external_id, origin=origin
|
||||
)
|
||||
except IntegrityError:
|
||||
response = {'result': 0, 'errors': [{'code': 'already-exists'}]}
|
||||
return Response(response, status.HTTP_400_BAD_REQUEST)
|
||||
for attachment in attachments:
|
||||
models.ValidationRequestAttachment.objects.create(
|
||||
validation_request=validation_request,
|
||||
image=attachment['b64_content'])
|
||||
return Response({
|
||||
'result': 1,
|
||||
'status': 'received',
|
||||
'sub': uuid,
|
||||
'id': validation_request.id,
|
||||
'external_id': external_id,
|
||||
}, status=status.HTTP_201_CREATED)
|
||||
validation_request=validation_request, image=attachment['b64_content']
|
||||
)
|
||||
return Response(
|
||||
{
|
||||
'result': 1,
|
||||
'status': 'received',
|
||||
'sub': uuid,
|
||||
'id': validation_request.id,
|
||||
'external_id': external_id,
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
else:
|
||||
errors = []
|
||||
for key in serializer.errors:
|
||||
|
@ -170,6 +182,7 @@ def validate_cut(self, request, uuid):
|
|||
response = {'result': 0, 'errors': errors}
|
||||
return Response(response, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
# attach new API to users' api
|
||||
api_views.UsersAPI.validate_cut = validate_cut
|
||||
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
class AppSettings(object):
|
||||
class AppSettings:
|
||||
'''Thanks django-allauth'''
|
||||
|
||||
__SENTINEL = object()
|
||||
|
||||
def __init__(self, prefix):
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# authentic2_cut - Authentic2 plugin for CUT
|
||||
# Copyright (C) 2016 Entr'ouvert
|
||||
|
@ -20,14 +19,11 @@ import copy
|
|||
import logging
|
||||
|
||||
import django.apps
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import router, DEFAULT_DB_ALIAS
|
||||
from django.utils.timezone import now, utc
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
from authentic2.constants import AUTHENTICATION_EVENTS_SESSION_KEY
|
||||
from django.conf import settings
|
||||
from django.db import DEFAULT_DB_ALIAS, router
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.timezone import now, utc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -43,23 +39,21 @@ class AppConfig(django.apps.AppConfig):
|
|||
if not router.allow_migrate(using, Operation):
|
||||
return
|
||||
|
||||
FC_MANAGE_OP = Operation.register(name=u'Gérer FranceConnect', slug='fc_manage')
|
||||
FC_MANAGE_OP = Operation.register(name='Gérer FranceConnect', slug='fc_manage')
|
||||
get_operation(FC_MANAGE_OP)
|
||||
|
||||
def ready(self):
|
||||
from django.db.models.signals import post_migrate, post_save
|
||||
from django_rbac.utils import get_ou_model
|
||||
|
||||
from . import api_views
|
||||
|
||||
post_migrate.connect(
|
||||
self.post_migrate,
|
||||
sender=self)
|
||||
post_save.connect(
|
||||
self.ou_post_save,
|
||||
sender=get_ou_model())
|
||||
post_migrate.connect(self.post_migrate, sender=self)
|
||||
post_save.connect(self.ou_post_save, sender=get_ou_model())
|
||||
|
||||
def ou_post_save(self, sender, instance, raw, created, **kwargs):
|
||||
from .utils import update_roles
|
||||
|
||||
update_roles()
|
||||
|
||||
def a2_hook_manager_user_data(self, view, user):
|
||||
|
@ -73,14 +67,12 @@ class AppConfig(django.apps.AppConfig):
|
|||
FranceConnectUserData(user, view.request),
|
||||
)
|
||||
if user.attributes.validated:
|
||||
user_datas.append(
|
||||
ValidationUserData(user)
|
||||
)
|
||||
user_datas.append(ValidationUserData(user))
|
||||
return user_datas
|
||||
|
||||
def a2_hook_manager_modify_form(self, view, form):
|
||||
from django.forms.widgets import DateTimeInput, HiddenInput, Textarea
|
||||
from authentic2.passwords import generate_password
|
||||
from django.forms.widgets import DateTimeInput, HiddenInput, Textarea
|
||||
|
||||
from . import models
|
||||
|
||||
|
@ -95,8 +87,14 @@ class AppConfig(django.apps.AppConfig):
|
|||
form.fields['password2'].widget = HiddenInput()
|
||||
if ou.slug == 'usagers':
|
||||
del form.fields['username']
|
||||
for field_name in ['is_superuser', 'validated',
|
||||
'validation_context', 'phone', 'address', 'validation_date']:
|
||||
for field_name in [
|
||||
'is_superuser',
|
||||
'validated',
|
||||
'validation_context',
|
||||
'phone',
|
||||
'address',
|
||||
'validation_date',
|
||||
]:
|
||||
if field_name in form.fields:
|
||||
del form.fields[field_name]
|
||||
|
||||
|
@ -111,19 +109,22 @@ class AppConfig(django.apps.AppConfig):
|
|||
form.fields['creation_mode'].initial = 'backoffice'
|
||||
form.fields['creation_mode'].widget = HiddenInput()
|
||||
form.fields['creation_partner'].initial = (
|
||||
view.request.user.ou.name if view.request.user.ou else 'super-utilisateur')
|
||||
view.request.user.ou.name if view.request.user.ou else 'super-utilisateur'
|
||||
)
|
||||
form.fields['creation_partner'].widget = HiddenInput()
|
||||
form.fields['comment'].widget = Textarea(attrs={'rows': 4})
|
||||
del form.fields['creation_domain']
|
||||
del form.fields['validation_partner']
|
||||
else:
|
||||
for field_name in list(form.fields):
|
||||
if field_name not in ['username',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'send_password_reset',
|
||||
'generate_password']:
|
||||
if field_name not in [
|
||||
'username',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'send_password_reset',
|
||||
'generate_password',
|
||||
]:
|
||||
del form.fields[field_name]
|
||||
form.fields['email'].required = True
|
||||
form.fields['generate_password'].initial = False
|
||||
|
@ -139,20 +140,29 @@ class AppConfig(django.apps.AppConfig):
|
|||
del form.fields['validation_partner']
|
||||
if form.instance.ou and form.instance.ou.slug == 'usagers':
|
||||
del form.fields['username']
|
||||
for field_name in ['password1', 'password2', 'is_superuser', 'validated',
|
||||
'validation_context', 'validation_date', 'ou']:
|
||||
for field_name in [
|
||||
'password1',
|
||||
'password2',
|
||||
'is_superuser',
|
||||
'validated',
|
||||
'validation_context',
|
||||
'validation_date',
|
||||
'ou',
|
||||
]:
|
||||
if field_name in form.fields:
|
||||
del form.fields[field_name]
|
||||
form.fields['comment'].widget = Textarea(attrs={'rows': 4})
|
||||
else:
|
||||
for field_name in list(form.fields):
|
||||
if field_name not in ['username',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'is_superuser',
|
||||
'generate_password',
|
||||
'ou']:
|
||||
if field_name not in [
|
||||
'username',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'is_superuser',
|
||||
'generate_password',
|
||||
'ou',
|
||||
]:
|
||||
del form.fields[field_name]
|
||||
# do not allow to move an agent in the usagers OU
|
||||
form.fields['ou'].queryset = form.fields['ou'].queryset.exclude(slug='usagers')
|
||||
|
@ -163,20 +173,25 @@ class AppConfig(django.apps.AppConfig):
|
|||
user = form.instance
|
||||
user.roles.remove(*list(user.roles.exclude(ou=form.instance.ou)))
|
||||
return response
|
||||
|
||||
return edit_save
|
||||
|
||||
form.save = generate_new_edit_save(form.save)
|
||||
|
||||
# Si un compte est validé, on interdit la modification des attributs coeurs
|
||||
if form.instance.attributes.validated:
|
||||
for field_name in list(form.fields):
|
||||
if field_name in ['first_name',
|
||||
'last_name',
|
||||
'birthcountry_insee',
|
||||
'birthplace_insee',
|
||||
'birthcountry',
|
||||
'birthplace',
|
||||
'gender', 'title',
|
||||
'birthdate']:
|
||||
if field_name in [
|
||||
'first_name',
|
||||
'last_name',
|
||||
'birthcountry_insee',
|
||||
'birthplace_insee',
|
||||
'birthcountry',
|
||||
'birthplace',
|
||||
'gender',
|
||||
'title',
|
||||
'birthdate',
|
||||
]:
|
||||
# del form.fields[field_name]
|
||||
field = form.fields[field_name]
|
||||
field.required = False
|
||||
|
@ -184,7 +199,7 @@ class AppConfig(django.apps.AppConfig):
|
|||
field.widget = DateTimeInput(attrs={'readonly': ''})
|
||||
attrs = field.widget.attrs or {}
|
||||
attrs['disabled'] = ''
|
||||
attrs['title'] = u'Champ validé'
|
||||
attrs['title'] = 'Champ validé'
|
||||
field.widget.attrs = attrs
|
||||
|
||||
def new_clean(self, field_name):
|
||||
|
@ -194,7 +209,9 @@ class AppConfig(django.apps.AppConfig):
|
|||
return getattr(self.instance, field_name)
|
||||
else:
|
||||
return getattr(self.instance.attributes, field_name)
|
||||
|
||||
return clean
|
||||
|
||||
setattr(form, 'clean_' + field_name, new_clean(form, field_name))
|
||||
if field_name in ['generate_password']:
|
||||
del form.fields[field_name]
|
||||
|
@ -202,9 +219,16 @@ class AppConfig(django.apps.AppConfig):
|
|||
if view.__class__.__name__.endswith('UserDetailView'):
|
||||
if form.instance.ou:
|
||||
if form.instance.ou.slug == 'usagers':
|
||||
for field_name in ['username', 'is_superuser', 'validated',
|
||||
'validation_context', 'validation_date', 'creation_mode',
|
||||
'creation_partner', 'creation_domain']:
|
||||
for field_name in [
|
||||
'username',
|
||||
'is_superuser',
|
||||
'validated',
|
||||
'validation_context',
|
||||
'validation_date',
|
||||
'creation_mode',
|
||||
'creation_partner',
|
||||
'creation_domain',
|
||||
]:
|
||||
if field_name in form.fields:
|
||||
del form.fields[field_name]
|
||||
form.fields['comment'].widget = Textarea(attrs={'readonly': '', 'rows': 4})
|
||||
|
@ -213,8 +237,11 @@ class AppConfig(django.apps.AppConfig):
|
|||
if field_name not in ['username', 'first_name', 'last_name', 'email']:
|
||||
del form.fields[field_name]
|
||||
|
||||
if view.__class__.__name__ in ['OrganizationalUnitDetailView', 'OrganizationalUnitEditView',
|
||||
'OrganizationalUnitAddView']:
|
||||
if view.__class__.__name__ in [
|
||||
'OrganizationalUnitDetailView',
|
||||
'OrganizationalUnitEditView',
|
||||
'OrganizationalUnitAddView',
|
||||
]:
|
||||
del form.fields['default']
|
||||
del form.fields['email_is_unique']
|
||||
del form.fields['username_is_unique']
|
||||
|
@ -236,10 +263,12 @@ class AppConfig(django.apps.AppConfig):
|
|||
sequence.remove('email')
|
||||
sequence.insert(2, 'email')
|
||||
base_columns['preferred_username'] = tables.Column(
|
||||
accessor='attributes.preferred_username', verbose_name=u'Nom d\'usage')
|
||||
accessor='attributes.preferred_username', verbose_name='Nom d\'usage'
|
||||
)
|
||||
sequence.insert(2, 'preferred_username')
|
||||
base_columns['validated'] = tables.BooleanColumn(
|
||||
accessor='attributes.validated', verbose_name=u'Validé')
|
||||
accessor='attributes.validated', verbose_name='Validé'
|
||||
)
|
||||
sequence += ['validated']
|
||||
else:
|
||||
del base_columns['link']
|
||||
|
@ -276,20 +305,21 @@ class AppConfig(django.apps.AppConfig):
|
|||
except AttributeError:
|
||||
return None
|
||||
return {'Monsieur': 'male', 'Madame': 'female'}.get(title)
|
||||
|
||||
serializer.get_gender = get_gender
|
||||
|
||||
serializer.fields['gender'] = serializers.SerializerMethodField()
|
||||
serializer.fields['given_name'] = serializers.CharField(read_only=True,
|
||||
source='first_name')
|
||||
serializer.fields['family_name'] = serializers.CharField(read_only=True,
|
||||
source='last_name')
|
||||
serializer.fields['given_name'] = serializers.CharField(read_only=True, source='first_name')
|
||||
serializer.fields['family_name'] = serializers.CharField(read_only=True, source='last_name')
|
||||
serializer.fields['email_verified'].read_only = True
|
||||
serializer.fields['validation_context'] = serializers.ChoiceField(
|
||||
source='attributes.validation_context',
|
||||
choices=[
|
||||
('online', 'online'),
|
||||
('office', 'office'),
|
||||
], required=False)
|
||||
],
|
||||
required=False,
|
||||
)
|
||||
serializer.fields['validation_partner'].read_only = True
|
||||
serializer.fields['creation_mode'].read_only = True
|
||||
serializer.fields['creation_partner'].read_only = True
|
||||
|
@ -298,12 +328,14 @@ class AppConfig(django.apps.AppConfig):
|
|||
def get_address_fc(obj):
|
||||
if obj.fc_accounts.all():
|
||||
return obj.fc_accounts.all()[0].get_user_info().get('address')
|
||||
|
||||
serializer.get_address_fc = get_address_fc
|
||||
serializer.fields['address_fc'] = serializers.SerializerMethodField()
|
||||
|
||||
def get_phone_number_fc(obj):
|
||||
if obj.fc_accounts.all():
|
||||
return obj.fc_accounts.all()[0].get_user_info().get('phone_number')
|
||||
|
||||
serializer.get_phone_number_fc = get_phone_number_fc
|
||||
serializer.fields['phone_number_fc'] = serializers.SerializerMethodField()
|
||||
|
||||
|
@ -322,6 +354,7 @@ class AppConfig(django.apps.AppConfig):
|
|||
instance.attributes.creation_partner = 'UNKNOWN'
|
||||
instance.attributes.creation_mode = 'BO'
|
||||
return instance
|
||||
|
||||
serializer.create = new_create
|
||||
|
||||
old_update = serializer.update
|
||||
|
@ -338,6 +371,7 @@ class AppConfig(django.apps.AppConfig):
|
|||
raise NotImplementedError
|
||||
instance.attributes.validation_partner = partner
|
||||
return instance
|
||||
|
||||
serializer.update = new_update
|
||||
|
||||
validation_variables = ['validated', 'validation_context', 'validation_date']
|
||||
|
@ -351,13 +385,14 @@ class AppConfig(django.apps.AppConfig):
|
|||
if set(attributes) & set(validation_variables):
|
||||
if instance.attributes.validated:
|
||||
raise serializers.ValidationError(
|
||||
'account already validated you cannot modify the validation status')
|
||||
'account already validated you cannot modify the validation status'
|
||||
)
|
||||
if not set(validation_variables).issubset(set(attributes)):
|
||||
raise serializers.ValidationError(
|
||||
'validated, validation_context and validation_date are required together')
|
||||
'validated, validation_context and validation_date are required together'
|
||||
)
|
||||
if attributes['validated'] is not True:
|
||||
raise serializers.ValidationError(
|
||||
'validated can only be true')
|
||||
raise serializers.ValidationError('validated can only be true')
|
||||
request = serializer.context['request']
|
||||
if hasattr(request.user, 'oidc_client'):
|
||||
pass
|
||||
|
@ -367,12 +402,12 @@ class AppConfig(django.apps.AppConfig):
|
|||
if set(attributes) & set(validation_variables):
|
||||
raise serializers.ValidationError('you cannot validate during creation')
|
||||
return data
|
||||
|
||||
serializer.validate = new_validate
|
||||
|
||||
serializer.fields['modified'].timezone = utc
|
||||
serializer.fields['date_joined'].timezone = utc
|
||||
|
||||
|
||||
# execute after other modifiers
|
||||
a2_hook_api_modify_serializer.order = 999
|
||||
|
||||
|
@ -380,13 +415,16 @@ class AppConfig(django.apps.AppConfig):
|
|||
from .custom_settings import CORE_ATTRIBUTES, CROWN_ATTRIBUTES
|
||||
|
||||
if view.__class__.__name__ == 'ProfileView':
|
||||
context['cut_core_filled'] = all(getattr(view.request.user.attributes, a, None) for a in
|
||||
CORE_ATTRIBUTES)
|
||||
context['cut_crown_filled'] = any(getattr(view.request.user.attributes, a, None) for a
|
||||
in CROWN_ATTRIBUTES)
|
||||
context['cut_core_filled'] = all(
|
||||
getattr(view.request.user.attributes, a, None) for a in CORE_ATTRIBUTES
|
||||
)
|
||||
context['cut_crown_filled'] = any(
|
||||
getattr(view.request.user.attributes, a, None) for a in CROWN_ATTRIBUTES
|
||||
)
|
||||
|
||||
def a2_hook_manager_modify_other_actions(self, view, other_actions):
|
||||
from authentic2.manager.views import Action
|
||||
|
||||
from .actions import RemoveFranceConnect
|
||||
|
||||
class CUTValidate(Action):
|
||||
|
@ -402,9 +440,9 @@ class AppConfig(django.apps.AppConfig):
|
|||
if user.attributes.validated and user.attributes.validation_context == 'fc':
|
||||
return False
|
||||
if user.attributes.validated:
|
||||
self.title = u'Modifier les données coeur'
|
||||
self.title = 'Modifier les données coeur'
|
||||
self.user = user
|
||||
return super(CUTValidate, self).display(user, request)
|
||||
return super().display(user, request)
|
||||
|
||||
class CUTJournalActions(Action):
|
||||
name = 'cut-journal-actions'
|
||||
|
@ -431,8 +469,7 @@ class AppConfig(django.apps.AppConfig):
|
|||
|
||||
if view.__class__.__name__ == 'EditProfile':
|
||||
if form.instance and form.instance.attributes.validated:
|
||||
for field in ('first_name', 'last_name', 'birthdate', 'title',
|
||||
'birthplace', 'birthcountry'):
|
||||
for field in ('first_name', 'last_name', 'birthdate', 'title', 'birthplace', 'birthcountry'):
|
||||
form.fields.pop(field, None)
|
||||
for field in form.fields.values():
|
||||
if hasattr(field, 'max_length'):
|
||||
|
@ -450,9 +487,10 @@ class AppConfig(django.apps.AppConfig):
|
|||
form.fields.pop(name)
|
||||
|
||||
def a2_hook_api_modify_queryset(self, view, queryset):
|
||||
from django.utils.timezone import now
|
||||
from datetime import date
|
||||
|
||||
from django.utils.timezone import now
|
||||
|
||||
def majority(today):
|
||||
for i in range(5):
|
||||
try:
|
||||
|
@ -464,16 +502,14 @@ class AppConfig(django.apps.AppConfig):
|
|||
if view.__class__.__name__ == 'UsersAPI':
|
||||
if hasattr(view.request.user, 'oidc_client'):
|
||||
oidc_client = view.request.user.oidc_client
|
||||
hide_underaged_oidc_client = getattr(settings,
|
||||
'A2_CUT_HIDE_UNDERAGED_CLIENT_IDS', [])
|
||||
hide_underaged_oidc_client = getattr(settings, 'A2_CUT_HIDE_UNDERAGED_CLIENT_IDS', [])
|
||||
# Cache les comptes des mineurs pour les clients OIDC listés
|
||||
# dans A2_CUT_HIDE_UNDERAGED_CLIENT_IDS
|
||||
if oidc_client.client_id in hide_underaged_oidc_client:
|
||||
from authentic2.models import AttributeValue
|
||||
|
||||
before = majority(now().date()).isoformat()
|
||||
excluded = AttributeValue.objects.filter(
|
||||
attribute__name='birthdate',
|
||||
content__gt=before)
|
||||
excluded = AttributeValue.objects.filter(attribute__name='birthdate', content__gt=before)
|
||||
queryset = queryset.exclude(attribute_values__in=excluded)
|
||||
queryset = queryset.filter(ou__slug='usagers')
|
||||
return queryset
|
||||
|
@ -490,17 +526,16 @@ class AppConfig(django.apps.AppConfig):
|
|||
user_info['given_name'] = user.first_name
|
||||
user_info['family_name'] = user.last_name
|
||||
user_info['title'] = user.attributes.title
|
||||
user_info['gender'] = {'Monsieur': 'male', 'Madame': 'female'}.get(
|
||||
user.attributes.title)
|
||||
user_info['birthdate'] = (user.attributes.birthdate and
|
||||
user.attributes.birthdate.isoformat())
|
||||
user_info['gender'] = {'Monsieur': 'male', 'Madame': 'female'}.get(user.attributes.title)
|
||||
user_info['birthdate'] = user.attributes.birthdate and user.attributes.birthdate.isoformat()
|
||||
user_info['birthplace'] = user.attributes.birthplace
|
||||
user_info['birthcountry'] = user.attributes.birthcountry
|
||||
user_info['birthplace_insee'] = user.attributes.birthplace_insee
|
||||
user_info['birthcountry_insee'] = user.attributes.birthcountry_insee
|
||||
user_info['validated'] = user.attributes.validated
|
||||
user_info['validation_date'] = (user.attributes.validation_date and
|
||||
user.attributes.validation_date.isoformat())
|
||||
user_info['validation_date'] = (
|
||||
user.attributes.validation_date and user.attributes.validation_date.isoformat()
|
||||
)
|
||||
user_info['validation_context'] = user.attributes.validation_context
|
||||
# pass user.ou.slug for agent's users
|
||||
if user.ou:
|
||||
|
@ -508,11 +543,19 @@ class AppConfig(django.apps.AppConfig):
|
|||
user_info['ou'] = user.ou.slug
|
||||
else:
|
||||
if user.attributes.validated:
|
||||
for name in ['first_name', 'last_name',
|
||||
'given_name', 'family_name', 'title',
|
||||
'gender', 'birthdate',
|
||||
'birthplace_insee', 'birthplace',
|
||||
'birthcountry_insee', 'birthcountry']:
|
||||
for name in [
|
||||
'first_name',
|
||||
'last_name',
|
||||
'given_name',
|
||||
'family_name',
|
||||
'title',
|
||||
'gender',
|
||||
'birthdate',
|
||||
'birthplace_insee',
|
||||
'birthplace',
|
||||
'birthcountry_insee',
|
||||
'birthcountry',
|
||||
]:
|
||||
user_info['%s_verified' % name] = True
|
||||
if 'crown' in scope_set:
|
||||
user_info['preferred_username'] = user.attributes.preferred_username
|
||||
|
@ -551,17 +594,13 @@ class AppConfig(django.apps.AppConfig):
|
|||
|
||||
def log_action(self, actor, message):
|
||||
from . import models
|
||||
models.Journal.objects.create(
|
||||
actor=actor,
|
||||
message=message)
|
||||
|
||||
models.Journal.objects.create(actor=actor, message=message)
|
||||
|
||||
def log_modification(self, actor, subject, message, mail=True, mail_message=None):
|
||||
from . import models
|
||||
|
||||
models.Journal.objects.create(
|
||||
actor=actor,
|
||||
subject=subject,
|
||||
message=message)
|
||||
models.Journal.objects.create(actor=actor, subject=subject, message=message)
|
||||
# pour les modifications sur les usagers on envoie un mail à l'usager
|
||||
if mail:
|
||||
self.mail_notification(actor, subject, mail_message or message)
|
||||
|
@ -570,16 +609,20 @@ class AppConfig(django.apps.AppConfig):
|
|||
from authentic2.utils.misc import send_templated_mail
|
||||
|
||||
if subject.ou and subject.ou.slug == 'usagers':
|
||||
send_templated_mail(subject, 'authentic2/cut-notify-usager-modification',
|
||||
context={
|
||||
'message': message,
|
||||
'usager': subject,
|
||||
'agent': actor,
|
||||
})
|
||||
send_templated_mail(
|
||||
subject,
|
||||
'authentic2/cut-notify-usager-modification',
|
||||
context={
|
||||
'message': message,
|
||||
'usager': subject,
|
||||
'agent': actor,
|
||||
},
|
||||
)
|
||||
|
||||
@property
|
||||
def redis_client(self):
|
||||
from django.core import cache
|
||||
|
||||
return cache.cache.client.get_client()
|
||||
|
||||
def stat(self, *args):
|
||||
|
@ -598,17 +641,17 @@ class AppConfig(django.apps.AppConfig):
|
|||
method = how.upper()
|
||||
msg = self.get_authentication_message(how)
|
||||
if service:
|
||||
msg += u' pour %s' % service
|
||||
msg += ' pour %s' % service
|
||||
self.log_action(user, msg)
|
||||
self.stat('login', method)
|
||||
|
||||
def cut_event_registration(self, user, view, form, token, service, **kwargs):
|
||||
# log registration for current user
|
||||
creation_mode = 'FO'
|
||||
msg = u'création du compte en ligne'
|
||||
msg = 'création du compte en ligne'
|
||||
if 'franceconnect' in token:
|
||||
creation_mode = 'FC'
|
||||
msg = u'création du compte via FranceConnect'
|
||||
msg = 'création du compte via FranceConnect'
|
||||
user.attributes.creation_mode = creation_mode
|
||||
self.log_action(user, msg)
|
||||
if service:
|
||||
|
@ -617,12 +660,13 @@ class AppConfig(django.apps.AppConfig):
|
|||
def cut_event_sso_request(self, idp, service, **kwargs):
|
||||
self.stat('sso-request', service.slug)
|
||||
from authentic2.middleware import StoreRequestMiddleware
|
||||
|
||||
request = StoreRequestMiddleware.get_request()
|
||||
if request:
|
||||
request.session['service_slug'] = service.slug
|
||||
|
||||
def cut_event_sso_success(self, idp, service, user, **kwargs):
|
||||
msg = u'connexion à %s' % service.name
|
||||
msg = 'connexion à %s' % service.name
|
||||
self.log_action(user, msg)
|
||||
how = self.get_authentication_how()
|
||||
method = self.get_authentication_method(how)
|
||||
|
@ -631,41 +675,40 @@ class AppConfig(django.apps.AppConfig):
|
|||
def cut_event_cut_edit_core(self, user, form, **kwargs):
|
||||
if not form.has_changed():
|
||||
return
|
||||
self.log_action(user, u'édition du profil coeur')
|
||||
self.log_action(user, 'édition du profil coeur')
|
||||
|
||||
def cut_event_cut_edit_crown(self, user, form, **kwargs):
|
||||
if not form.has_changed():
|
||||
return
|
||||
self.log_action(user, u'édition du profil couronne')
|
||||
self.log_action(user, 'édition du profil couronne')
|
||||
|
||||
def cut_event_password_reset_confirm(self, user, **kwargs):
|
||||
self.log_action(user, u'ré-initialisation du mot de passe')
|
||||
self.log_action(user, 'ré-initialisation du mot de passe')
|
||||
|
||||
def cut_event_change_email_confirm(self, user, **kwargs):
|
||||
self.log_action(user, u'changement de l\'adresse de courriel')
|
||||
self.log_action(user, 'changement de l\'adresse de courriel')
|
||||
|
||||
def cut_event_delete_account(self, user, **kwargs):
|
||||
self.log_action(user, u'demande de suppression du compte')
|
||||
self.log_action(user, 'demande de suppression du compte')
|
||||
|
||||
def cut_event_change_password(self, user, **kwargs):
|
||||
self.log_action(user, u'changement du mot de passe')
|
||||
self.log_action(user, 'changement du mot de passe')
|
||||
|
||||
def cut_event_manager_action(self, user, action, instance, **kwargs):
|
||||
msgs = {
|
||||
'activate': u'ré-activation du compte',
|
||||
'deactivate': u'suspension du compte',
|
||||
'password_reset': u'envoi d\'un courriel de ré-initialisation du mot de passe',
|
||||
'force_password_change': u'force un changement de mot de passe à '
|
||||
u'la prochaine connexion',
|
||||
'delete_password_reset': u'supprime l\'obligation de changement de mot de passe à '
|
||||
u'la prochaine connexion',
|
||||
'remove-franceconnect': u'suppression de la liaison FranceConnect',
|
||||
'activate': 'ré-activation du compte',
|
||||
'deactivate': 'suspension du compte',
|
||||
'password_reset': 'envoi d\'un courriel de ré-initialisation du mot de passe',
|
||||
'force_password_change': 'force un changement de mot de passe à ' 'la prochaine connexion',
|
||||
'delete_password_reset': 'supprime l\'obligation de changement de mot de passe à '
|
||||
'la prochaine connexion',
|
||||
'remove-franceconnect': 'suppression de la liaison FranceConnect',
|
||||
}
|
||||
if action.name in msgs:
|
||||
self.log_modification(user, instance, msgs[action.name])
|
||||
|
||||
def cut_event_manager_add_user(self, user, instance, **kwargs):
|
||||
self.log_modification(user, instance, u'création d\'un utilisateur')
|
||||
self.log_modification(user, instance, 'création d\'un utilisateur')
|
||||
|
||||
def cut_event_manager_edit_user(self, user, instance, form, **kwargs):
|
||||
if not form.has_changed():
|
||||
|
@ -675,47 +718,46 @@ class AppConfig(django.apps.AppConfig):
|
|||
# vérifie la présence d'un attribut couronne dans le formulaire
|
||||
if 'preferred_username' in form.fields:
|
||||
if instance.attributes.validated:
|
||||
self.log_modification(user, instance, u'modification du profil couronne')
|
||||
self.log_modification(user, instance, 'modification du profil couronne')
|
||||
else:
|
||||
self.log_modification(user, instance,
|
||||
u'modification du profil coeur et couronne')
|
||||
self.log_modification(user, instance, 'modification du profil coeur et couronne')
|
||||
# si on est dans le formulaire coeur on ne log rien
|
||||
else:
|
||||
self.log_modification(user, instance, u'modification du profil')
|
||||
self.log_modification(user, instance, 'modification du profil')
|
||||
|
||||
def cut_event_manager_delete_user(self, user, instance, **kwargs):
|
||||
self.log_action(user, u'suppression de l\'utilisateur %s' % instance)
|
||||
self.mail_notification(user, instance, u'suppression de votre compte')
|
||||
self.log_action(user, 'suppression de l\'utilisateur %s' % instance)
|
||||
self.mail_notification(user, instance, 'suppression de votre compte')
|
||||
|
||||
def cut_event_manager_add_role_member(self, user, role, member, **kwargs):
|
||||
self.log_modification(user, member, u'ajoute le rôle %s' % role)
|
||||
self.log_modification(user, member, 'ajoute le rôle %s' % role)
|
||||
|
||||
def cut_event_manager_remove_role_member(self, user, role, member, **kwargs):
|
||||
self.log_modification(user, member, u'retire le rôle %s' % role)
|
||||
self.log_modification(user, member, 'retire le rôle %s' % role)
|
||||
|
||||
def cut_event_manager_cut_validate(self, user, instance, context, partner, **kwargs):
|
||||
if instance.attributes.validated:
|
||||
msg = u'modification du profil cœur'
|
||||
msg = 'modification du profil cœur'
|
||||
else:
|
||||
msg = u'validation du compte'
|
||||
msg = 'validation du compte'
|
||||
mail_msg = msg
|
||||
msg += u', contexte "%s" venant de %s' % (context, partner)
|
||||
msg += ', contexte "%s" venant de %s' % (context, partner)
|
||||
self.log_modification(user, instance, msg, mail_message=mail_msg)
|
||||
|
||||
def cut_event_manager_view_user(self, user, instance, **kwargs):
|
||||
self.log_modification(user, instance, u'fiche consultée', mail=False)
|
||||
self.log_modification(user, instance, 'fiche consultée', mail=False)
|
||||
|
||||
def cut_event_manager_change_password(self, user, instance, form, **kwargs):
|
||||
if form.cleaned_data.get('generate_password'):
|
||||
msg = u'génération d\'un nouveau mot de passe'
|
||||
msg = 'génération d\'un nouveau mot de passe'
|
||||
else:
|
||||
msg = u'changement du mot de passe'
|
||||
msg = 'changement du mot de passe'
|
||||
if form.cleaned_data.get('send_mail'):
|
||||
msg += u' envoyé par mail'
|
||||
msg += ' envoyé par mail'
|
||||
self.log_modification(user, instance, msg)
|
||||
|
||||
def cut_event_fc_link(self, user, request, **kwargs):
|
||||
self.log_action(user, u'liaison avec FranceConnect')
|
||||
self.log_action(user, 'liaison avec FranceConnect')
|
||||
user.attributes.validated = True
|
||||
user.attributes.validation_context = 'FC'
|
||||
user.attributes.validation_date = now().date()
|
||||
|
@ -726,10 +768,11 @@ class AppConfig(django.apps.AppConfig):
|
|||
user.attributes.validation_partner = partner
|
||||
|
||||
def cut_event_fc_unlink(self, user, **kwargs):
|
||||
self.log_action(user, u'déliaison de FranceConnect')
|
||||
self.log_action(user, 'déliaison de FranceConnect')
|
||||
|
||||
def get_authentication_how(self):
|
||||
from authentic2.middleware import StoreRequestMiddleware
|
||||
|
||||
request = StoreRequestMiddleware.get_request()
|
||||
if request:
|
||||
for event in request.session.get(AUTHENTICATION_EVENTS_SESSION_KEY, []):
|
||||
|
@ -752,15 +795,15 @@ class AppConfig(django.apps.AppConfig):
|
|||
|
||||
def get_authentication_message(self, how):
|
||||
if how.startswith('password'):
|
||||
return u'connexion par mot de passe'
|
||||
return 'connexion par mot de passe'
|
||||
elif how == 'france-connect':
|
||||
return u'connexion par FranceConnect'
|
||||
return 'connexion par FranceConnect'
|
||||
elif how == 'email':
|
||||
return u'connexion à l\'enregistrement ou par récupération de mot de passe'
|
||||
return 'connexion à l\'enregistrement ou par récupération de mot de passe'
|
||||
elif how == 'oidc':
|
||||
return u'connexion'
|
||||
return 'connexion'
|
||||
else:
|
||||
return u'connexion how:%s' % how
|
||||
return 'connexion how:%s' % how
|
||||
|
||||
def a2_hook_manager_homepage_entries(self, view):
|
||||
return {
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from authentic2.settings import INSTALLED_APPS, CACHES
|
||||
import os
|
||||
|
||||
from authentic2.settings import CACHES, INSTALLED_APPS
|
||||
|
||||
PLATFORM = vars().get('PLATFORM')
|
||||
if PLATFORM not in ['dev', 'test', 'prod']:
|
||||
PLATFORM = 'prod'
|
||||
|
||||
CORE_ATTRIBUTES = [
|
||||
'title',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'birthdate',
|
||||
'birthplace',
|
||||
'birthcountry'
|
||||
]
|
||||
CORE_ATTRIBUTES = ['title', 'first_name', 'last_name', 'birthdate', 'birthplace', 'birthcountry']
|
||||
|
||||
CROWN_ATTRIBUTES = [
|
||||
'preferred_username',
|
||||
|
@ -28,7 +21,7 @@ CROWN_ATTRIBUTES = [
|
|||
'home_phone',
|
||||
'professional_mobile_phone',
|
||||
'professional_phone',
|
||||
'birthdepartment'
|
||||
'birthdepartment',
|
||||
]
|
||||
|
||||
# Manager
|
||||
|
@ -46,12 +39,12 @@ A2_RBAC_MANAGED_CONTENT_TYPES = ()
|
|||
A2_CUT_PARTNERS = [
|
||||
{
|
||||
'domains': ['.lyon.fr'],
|
||||
'name': u'Ville de Lyon',
|
||||
'name': 'Ville de Lyon',
|
||||
},
|
||||
{
|
||||
'domains': ['.entrouvert.org'],
|
||||
'name': u'Ville de Lyon',
|
||||
}
|
||||
'name': 'Ville de Lyon',
|
||||
},
|
||||
]
|
||||
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
|
@ -81,7 +74,7 @@ A2_FC_USER_INFO_MAPPINGS = {
|
|||
'translation_simple': {
|
||||
'male': 'Monsieur',
|
||||
'female': 'Madame',
|
||||
}
|
||||
},
|
||||
},
|
||||
'preferred_username': {
|
||||
'ref': 'preferred_username',
|
||||
|
@ -107,28 +100,34 @@ TEMPLATE_VARS = {
|
|||
"help_url": 'https://support.grandlyon.com/glc/',
|
||||
"account_label": "compte GRANDLYON CONNECT",
|
||||
"partners_url": "https://www.grandlyon.com/projets/metropole-intelligente.html",
|
||||
"support_url": "https://support.grandlyon.com/glc/#generalites"
|
||||
"support_url": "https://support.grandlyon.com/glc/#generalites",
|
||||
}
|
||||
if PLATFORM == 'dev':
|
||||
TEMPLATE_VARS.update({
|
||||
"environment_label": u"Site de développement",
|
||||
"moncompte_url": 'https://connexion-grandlyon.dev.entrouvert.org',
|
||||
"help_url": "https://portail-citoyen-sau.guichet-recette.grandlyon.com/glc/",
|
||||
})
|
||||
TEMPLATE_VARS.update(
|
||||
{
|
||||
"environment_label": "Site de développement",
|
||||
"moncompte_url": 'https://connexion-grandlyon.dev.entrouvert.org',
|
||||
"help_url": "https://portail-citoyen-sau.guichet-recette.grandlyon.com/glc/",
|
||||
}
|
||||
)
|
||||
elif PLATFORM == 'test':
|
||||
TEMPLATE_VARS.update({
|
||||
"environment_label": u"Site de test",
|
||||
"smarttag_js": "https://tag.aticdn.net/586793/smarttag.js",
|
||||
"visits_tracking_js": "var tag = new ATInternet.Tracker.Tag();",
|
||||
"moncompte_url": 'https://moncompte-rec.grandlyon.com',
|
||||
"help_url": "https://portail-citoyen-sau.guichet-recette.grandlyon.com/glc/",
|
||||
})
|
||||
TEMPLATE_VARS.update(
|
||||
{
|
||||
"environment_label": "Site de test",
|
||||
"smarttag_js": "https://tag.aticdn.net/586793/smarttag.js",
|
||||
"visits_tracking_js": "var tag = new ATInternet.Tracker.Tag();",
|
||||
"moncompte_url": 'https://moncompte-rec.grandlyon.com',
|
||||
"help_url": "https://portail-citoyen-sau.guichet-recette.grandlyon.com/glc/",
|
||||
}
|
||||
)
|
||||
elif PLATFORM == 'prod':
|
||||
TEMPLATE_VARS.update({
|
||||
"smarttag_js": "https://tag.aticdn.net/586794/smarttag.js",
|
||||
"visits_tracking_js": "var tag = new ATInternet.Tracker.Tag();",
|
||||
"moncompte_url": 'https://moncompte.grandlyon.com',
|
||||
})
|
||||
TEMPLATE_VARS.update(
|
||||
{
|
||||
"smarttag_js": "https://tag.aticdn.net/586794/smarttag.js",
|
||||
"visits_tracking_js": "var tag = new ATInternet.Tracker.Tag();",
|
||||
"moncompte_url": 'https://moncompte.grandlyon.com',
|
||||
}
|
||||
)
|
||||
|
||||
A2_HOMEPAGE_URL = '/accounts/'
|
||||
A2_MANAGER_SITE_TITLE = 'GRANDLYON CONNECT'
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# authentic2_cut - Authentic2 plugin for CUT
|
||||
# Copyright (C) 2017 Entr'ouvert
|
||||
#
|
||||
|
@ -19,8 +18,10 @@ from django import forms
|
|||
|
||||
from . import models
|
||||
|
||||
|
||||
class ValidationForm(forms.Form):
|
||||
reason = forms.ChoiceField(
|
||||
choices=(('', 'Aucune'),) + models.ValidationRequest.REASON_CHOICES,
|
||||
required=False,
|
||||
label=u'Raison du refus')
|
||||
label='Raison du refus',
|
||||
)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import os
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.urlresolvers import reverse
|
||||
from authentic2_idp_oidc.models import OIDCClient
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db.transaction import atomic
|
||||
|
||||
from authentic2_idp_oidc.models import OIDCClient
|
||||
from authentic2_cut.models import ValidationRequest, ValidationRequestAttachment
|
||||
|
||||
|
||||
|
@ -25,15 +25,11 @@ class Command(BaseCommand):
|
|||
user = User.objects.get(pk=user_pk)
|
||||
|
||||
with atomic():
|
||||
validation_request = ValidationRequest.objects.create(
|
||||
user=user,
|
||||
origin=oidc_client)
|
||||
validation_request = ValidationRequest.objects.create(user=user, origin=oidc_client)
|
||||
for path in paths:
|
||||
|
||||
with open(path) as file_object:
|
||||
filename = os.path.basename(path)
|
||||
f = ContentFile(file_object.read(), name=filename)
|
||||
ValidationRequestAttachment.objects.create(
|
||||
validation_request=validation_request,
|
||||
image=f)
|
||||
ValidationRequestAttachment.objects.create(validation_request=validation_request, image=f)
|
||||
self.stdout.write('New validation request %s' % validation_request.pk)
|
||||
|
|
|
@ -1,31 +1,31 @@
|
|||
from authentic2.utils.misc import redirect, same_domain
|
||||
from django.conf import settings
|
||||
from django.utils.six.moves.urllib import parse as urlparse
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
|
||||
from authentic2.utils.misc import same_domain, redirect
|
||||
from django.utils.six.moves.urllib import parse as urlparse
|
||||
|
||||
|
||||
class CUTMiddleware(MiddlewareMixin):
|
||||
'''Extract requesting domain from URL and set value on request and in session.
|
||||
|
||||
Domains extracted from ?next=<url> of ?redirect_uri=<url> parameters are matched against the
|
||||
domains list of domain patterns.
|
||||
Domains extracted from ?next=<url> of ?redirect_uri=<url> parameters are matched against the
|
||||
domains list of domain patterns.
|
||||
|
||||
Configuration:
|
||||
Configuration:
|
||||
|
||||
A2_CUT_PARTNERS = [
|
||||
{
|
||||
'domains': ['.lyon.fr'],
|
||||
'logo_url': 'https://www.lyon.fr/static/logo_cut.png',
|
||||
'color': '#452334',
|
||||
'name': u'Ville de Lyon',
|
||||
}
|
||||
}
|
||||
A2_CUT_PARTNERS = [
|
||||
{
|
||||
'domains': ['.lyon.fr'],
|
||||
'logo_url': 'https://www.lyon.fr/static/logo_cut.png',
|
||||
'color': '#452334',
|
||||
'name': u'Ville de Lyon',
|
||||
}
|
||||
}
|
||||
|
||||
Use in templates:
|
||||
Use in templates:
|
||||
|
||||
{% if request.partner %}{{ request.partner.name }}{% endif %}
|
||||
{% if request.partner %}{{ request.partner.name }}{% endif %}
|
||||
'''
|
||||
|
||||
MATCHES = {}
|
||||
|
||||
def process_request(self, request):
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -15,11 +12,33 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='Journal',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True, verbose_name='Horodatage', db_index=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
(
|
||||
'timestamp',
|
||||
models.DateTimeField(auto_now_add=True, verbose_name='Horodatage', db_index=True),
|
||||
),
|
||||
('message', models.TextField(verbose_name='Message')),
|
||||
('actor', models.ForeignKey(related_name='actor_journal', verbose_name='Auteur', to=settings.AUTH_USER_MODEL, on_delete=models.SET_NULL)),
|
||||
('subject', models.ForeignKey(related_name='subject_journal', verbose_name='Sujet', to=settings.AUTH_USER_MODEL, on_delete=models.SET_NULL)),
|
||||
(
|
||||
'actor',
|
||||
models.ForeignKey(
|
||||
related_name='actor_journal',
|
||||
verbose_name='Auteur',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.SET_NULL,
|
||||
),
|
||||
),
|
||||
(
|
||||
'subject',
|
||||
models.ForeignKey(
|
||||
related_name='subject_journal',
|
||||
verbose_name='Sujet',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.SET_NULL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-timestamp', '-id'),
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -15,6 +12,12 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='journal',
|
||||
name='subject',
|
||||
field=models.ForeignKey(related_name='subject_journal', verbose_name='Sujet', to=settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL),
|
||||
field=models.ForeignKey(
|
||||
related_name='subject_journal',
|
||||
verbose_name='Sujet',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -17,18 +14,80 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='ValidationRequest',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='Date de cr\xe9ation', db_index=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
(
|
||||
'created',
|
||||
models.DateTimeField(
|
||||
auto_now_add=True, verbose_name='Date de cr\xe9ation', db_index=True
|
||||
),
|
||||
),
|
||||
('origin_id', models.PositiveIntegerField(verbose_name='origin id')),
|
||||
('status', models.CharField(default='received', max_length=16, verbose_name='Statut', choices=[('received', 're\xe7ue'), ('accepted', 'accept\xe9e'), ('refused', 'refus\xe9e')])),
|
||||
('reason', models.TextField(blank=True, verbose_name='Raison du refus', choices=[('unreadable', 'pi\xe8ce(s) illisible(s)'), ('invalid', 'pi\xe8ce(s) invalides(s)'), ('underaged', 'invividu mineur')])),
|
||||
(
|
||||
'status',
|
||||
models.CharField(
|
||||
default='received',
|
||||
max_length=16,
|
||||
verbose_name='Statut',
|
||||
choices=[
|
||||
('received', 're\xe7ue'),
|
||||
('accepted', 'accept\xe9e'),
|
||||
('refused', 'refus\xe9e'),
|
||||
],
|
||||
),
|
||||
),
|
||||
(
|
||||
'reason',
|
||||
models.TextField(
|
||||
blank=True,
|
||||
verbose_name='Raison du refus',
|
||||
choices=[
|
||||
('unreadable', 'pi\xe8ce(s) illisible(s)'),
|
||||
('invalid', 'pi\xe8ce(s) invalides(s)'),
|
||||
('underaged', 'invividu mineur'),
|
||||
],
|
||||
),
|
||||
),
|
||||
('validated', models.DateTimeField(null=True, verbose_name='Date de validation')),
|
||||
('external_id', models.TextField(null=True, verbose_name='Identifiant externe', blank=True)),
|
||||
('taken', models.DateTimeField(null=True, verbose_name='En cours')),
|
||||
('origin_ct', models.ForeignKey(verbose_name='origin ct', to='contenttypes.ContentType', on_delete=models.CASCADE)),
|
||||
('taken_by', models.ForeignKey(related_name='validation_requests_taken', verbose_name='Trait\xe9 par', to=settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)),
|
||||
('user', models.ForeignKey(related_name='validation_requests', verbose_name='Utilisateur', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
|
||||
('validated_by', models.ForeignKey(related_name='validation_requests_validated', verbose_name='Valid\xe9/refus\xe9 par', to=settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)),
|
||||
(
|
||||
'origin_ct',
|
||||
models.ForeignKey(
|
||||
verbose_name='origin ct', to='contenttypes.ContentType', on_delete=models.CASCADE
|
||||
),
|
||||
),
|
||||
(
|
||||
'taken_by',
|
||||
models.ForeignKey(
|
||||
related_name='validation_requests_taken',
|
||||
verbose_name='Trait\xe9 par',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
),
|
||||
),
|
||||
(
|
||||
'user',
|
||||
models.ForeignKey(
|
||||
related_name='validation_requests',
|
||||
verbose_name='Utilisateur',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
(
|
||||
'validated_by',
|
||||
models.ForeignKey(
|
||||
related_name='validation_requests_validated',
|
||||
verbose_name='Valid\xe9/refus\xe9 par',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-created',),
|
||||
|
@ -39,9 +98,20 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='ValidationRequestAttachment',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
(
|
||||
'id',
|
||||
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
|
||||
),
|
||||
('image', models.ImageField(upload_to='', verbose_name='contenu')),
|
||||
('validation_request', models.ForeignKey(related_name='attachments', verbose_name='requ\xeate de validation', to='authentic2_cut.ValidationRequest', on_delete=models.CASCADE)),
|
||||
(
|
||||
'validation_request',
|
||||
models.ForeignKey(
|
||||
related_name='attachments',
|
||||
verbose_name='requ\xeate de validation',
|
||||
to='authentic2_cut.ValidationRequest',
|
||||
on_delete=models.CASCADE,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('pk',),
|
||||
|
@ -51,6 +121,6 @@ class Migration(migrations.Migration):
|
|||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='validationrequest',
|
||||
unique_together=set([('origin_ct', 'origin_id', 'user', 'external_id')]),
|
||||
unique_together={('origin_ct', 'origin_id', 'user', 'external_id')},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -15,7 +12,15 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='validationrequest',
|
||||
name='reason',
|
||||
field=models.TextField(blank=True, verbose_name='Raison du refus', choices=[('unreadable', 'Pi\xe8ce(s) illisible(s)'), ('invalid', 'Pi\xe8ce(s) invalides(s)'), ('underaged', 'Invividu mineur')]),
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
verbose_name='Raison du refus',
|
||||
choices=[
|
||||
('unreadable', 'Pi\xe8ce(s) illisible(s)'),
|
||||
('invalid', 'Pi\xe8ce(s) invalides(s)'),
|
||||
('underaged', 'Invividu mineur'),
|
||||
],
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='validationrequest',
|
||||
|
@ -25,6 +30,13 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='validationrequest',
|
||||
name='validated_by',
|
||||
field=models.ForeignKey(related_name='validation_requests_validated', verbose_name='Valid\xe9/refus\xe9 par', blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL),
|
||||
field=models.ForeignKey(
|
||||
related_name='validation_requests_validated',
|
||||
verbose_name='Valid\xe9/refus\xe9 par',
|
||||
blank=True,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-05-15 14:16
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -17,6 +15,12 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='journal',
|
||||
name='actor',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='actor_journal', to=settings.AUTH_USER_MODEL, verbose_name='Auteur'),
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='actor_journal',
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name='Auteur',
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,42 +1,36 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os.path
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.db.models.query import Q
|
||||
from django.utils.timezone import now
|
||||
from django.urls import reverse
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db import models
|
||||
from django.db.models.query import Q
|
||||
from django.db.models.signals import post_delete
|
||||
from django.dispatch import receiver
|
||||
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
|
||||
from sorl.thumbnail import get_thumbnail, delete
|
||||
from django.urls import reverse
|
||||
from django.utils.timezone import now
|
||||
from sorl.thumbnail import delete, get_thumbnail
|
||||
|
||||
|
||||
class Journal(models.Model):
|
||||
timestamp = models.DateTimeField(
|
||||
verbose_name=u'Horodatage',
|
||||
db_index=True,
|
||||
auto_now_add=True)
|
||||
timestamp = models.DateTimeField(verbose_name='Horodatage', db_index=True, auto_now_add=True)
|
||||
actor = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
null=True,
|
||||
verbose_name=u'Auteur',
|
||||
verbose_name='Auteur',
|
||||
related_name='actor_journal',
|
||||
on_delete=models.SET_NULL)
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
subject = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name='Sujet',
|
||||
null=True,
|
||||
related_name='subject_journal',
|
||||
on_delete=models.SET_NULL)
|
||||
message = models.TextField(
|
||||
verbose_name='Message')
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
message = models.TextField(verbose_name='Message')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'historique'
|
||||
|
@ -60,9 +54,10 @@ class ValidationRequestManager(models.Manager):
|
|||
# la demande est prise par <user>
|
||||
qs = qs.filter(
|
||||
Q(status=ValidationRequest.STATUS_RECEIVED)
|
||||
& (Q(taken__isnull=True)
|
||||
| Q(taken__lt=now() - timedelta(seconds=self.model.TAKEN_DELAY))
|
||||
| Q(taken_by=user)
|
||||
& (
|
||||
Q(taken__isnull=True)
|
||||
| Q(taken__lt=now() - timedelta(seconds=self.model.TAKEN_DELAY))
|
||||
| Q(taken_by=user)
|
||||
)
|
||||
)
|
||||
# on veut la prochaine demande à traiter
|
||||
|
@ -91,69 +86,54 @@ class ValidationRequest(models.Model):
|
|||
STATUS_REFUSED = 'refused'
|
||||
|
||||
STATUS_CHOICES = (
|
||||
(STATUS_RECEIVED, u'reçue'),
|
||||
(STATUS_ACCEPTED, u'acceptée'),
|
||||
(STATUS_REFUSED, u'refusée'),
|
||||
(STATUS_RECEIVED, 'reçue'),
|
||||
(STATUS_ACCEPTED, 'acceptée'),
|
||||
(STATUS_REFUSED, 'refusée'),
|
||||
)
|
||||
|
||||
REASON_UNREADABLE = 'unreadable'
|
||||
REASON_INVALID = 'invalid'
|
||||
REASON_UNDERAGED = 'underaged'
|
||||
REASON_CHOICES = (
|
||||
(REASON_UNREADABLE, u'Pièce(s) illisible(s)'),
|
||||
(REASON_INVALID, u'Pièce(s) invalides(s)'),
|
||||
(REASON_UNDERAGED, u'Invividu mineur'),
|
||||
(REASON_UNREADABLE, 'Pièce(s) illisible(s)'),
|
||||
(REASON_INVALID, 'Pièce(s) invalides(s)'),
|
||||
(REASON_UNDERAGED, 'Invividu mineur'),
|
||||
)
|
||||
|
||||
created = models.DateTimeField(
|
||||
verbose_name=u'Date de création',
|
||||
db_index=True,
|
||||
auto_now_add=True)
|
||||
created = models.DateTimeField(verbose_name='Date de création', db_index=True, auto_now_add=True)
|
||||
origin_ct = models.ForeignKey(
|
||||
'contenttypes.ContentType',
|
||||
verbose_name='origin ct',
|
||||
on_delete=models.CASCADE)
|
||||
origin_id = models.PositiveIntegerField(
|
||||
verbose_name='origin id')
|
||||
'contenttypes.ContentType', verbose_name='origin ct', on_delete=models.CASCADE
|
||||
)
|
||||
origin_id = models.PositiveIntegerField(verbose_name='origin id')
|
||||
origin = GenericForeignKey('origin_ct', 'origin_id')
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name=u'Utilisateur',
|
||||
verbose_name='Utilisateur',
|
||||
related_name='validation_requests',
|
||||
on_delete=models.CASCADE)
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=16,
|
||||
choices=STATUS_CHOICES,
|
||||
verbose_name=u'Statut',
|
||||
default=STATUS_RECEIVED)
|
||||
reason = models.TextField(
|
||||
choices=REASON_CHOICES,
|
||||
blank=True,
|
||||
verbose_name=u'Raison du refus')
|
||||
validated = models.DateTimeField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=u'Date de validation')
|
||||
max_length=16, choices=STATUS_CHOICES, verbose_name='Statut', default=STATUS_RECEIVED
|
||||
)
|
||||
reason = models.TextField(choices=REASON_CHOICES, blank=True, verbose_name='Raison du refus')
|
||||
validated = models.DateTimeField(null=True, blank=True, verbose_name='Date de validation')
|
||||
validated_by = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name=u'Validé/refusé par',
|
||||
verbose_name='Validé/refusé par',
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='validation_requests_validated',
|
||||
on_delete=models.SET_NULL)
|
||||
external_id = models.TextField(
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=u'Identifiant externe')
|
||||
taken = models.DateTimeField(
|
||||
verbose_name=u'En cours',
|
||||
null=True)
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
external_id = models.TextField(blank=True, null=True, verbose_name='Identifiant externe')
|
||||
taken = models.DateTimeField(verbose_name='En cours', null=True)
|
||||
taken_by = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
verbose_name=u'Traité par',
|
||||
verbose_name='Traité par',
|
||||
null=True,
|
||||
related_name='validation_requests_taken',
|
||||
on_delete=models.SET_NULL)
|
||||
on_delete=models.SET_NULL,
|
||||
)
|
||||
|
||||
objects = ValidationRequestManager()
|
||||
|
||||
|
@ -170,14 +150,14 @@ class ValidationRequest(models.Model):
|
|||
@property
|
||||
def human_status(self):
|
||||
if self.status == self.STATUS_REFUSED:
|
||||
return u'refusé'
|
||||
return 'refusé'
|
||||
elif self.status == self.STATUS_ACCEPTED:
|
||||
return u'accepté'
|
||||
return 'accepté'
|
||||
elif self.status == self.STATUS_RECEIVED:
|
||||
if self.is_taken:
|
||||
return u'en traitement'
|
||||
return 'en traitement'
|
||||
else:
|
||||
return u'reçu'
|
||||
return 'reçu'
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -198,23 +178,21 @@ class ValidationRequest(models.Model):
|
|||
self.save()
|
||||
|
||||
class Meta:
|
||||
verbose_name = u'Requête de validation'
|
||||
verbose_name_plural = u'Requêtes de validation'
|
||||
verbose_name = 'Requête de validation'
|
||||
verbose_name_plural = 'Requêtes de validation'
|
||||
ordering = ('-created',)
|
||||
unique_together = (
|
||||
('origin_ct', 'origin_id', 'user', 'external_id'),
|
||||
)
|
||||
unique_together = (('origin_ct', 'origin_id', 'user', 'external_id'),)
|
||||
|
||||
|
||||
class ValidationRequestAttachment(models.Model):
|
||||
validation_request = models.ForeignKey(
|
||||
ValidationRequest,
|
||||
verbose_name=u'requête de validation',
|
||||
verbose_name='requête de validation',
|
||||
related_name='attachments',
|
||||
on_delete=models.CASCADE)
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
image = models.ImageField(
|
||||
verbose_name='contenu')
|
||||
image = models.ImageField(verbose_name='contenu')
|
||||
|
||||
@property
|
||||
def extension(self):
|
||||
|
@ -224,10 +202,8 @@ class ValidationRequestAttachment(models.Model):
|
|||
def url(self):
|
||||
return reverse(
|
||||
'cut-manager-user-validation-attachment',
|
||||
kwargs={
|
||||
'pk': self.pk,
|
||||
'filename': self.image.name.rsplit('/', 1)[-1]
|
||||
})
|
||||
kwargs={'pk': self.pk, 'filename': self.image.name.rsplit('/', 1)[-1]},
|
||||
)
|
||||
|
||||
@property
|
||||
def thumbnail(self):
|
||||
|
@ -241,7 +217,7 @@ class ValidationRequestAttachment(models.Model):
|
|||
with default_storage.open(thumbnail.name):
|
||||
pass
|
||||
return thumbnail
|
||||
except IOError:
|
||||
except OSError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
@ -252,10 +228,7 @@ class ValidationRequestAttachment(models.Model):
|
|||
return {
|
||||
'src': reverse(
|
||||
'cut-manager-user-validation-attachment-thumbnail',
|
||||
kwargs={
|
||||
'pk': self.pk,
|
||||
'filename': self.image.name.rsplit('/', 1)[-1]
|
||||
}
|
||||
kwargs={'pk': self.pk, 'filename': self.image.name.rsplit('/', 1)[-1]},
|
||||
),
|
||||
'height': thumbnail.height,
|
||||
'width': thumbnail.width,
|
||||
|
@ -264,8 +237,8 @@ class ValidationRequestAttachment(models.Model):
|
|||
|
||||
class Meta:
|
||||
ordering = ('pk',)
|
||||
verbose_name = u'Pièce jointe'
|
||||
verbose_name_plural = u'Pièces jointes'
|
||||
verbose_name = 'Pièce jointe'
|
||||
verbose_name_plural = 'Pièces jointes'
|
||||
|
||||
|
||||
@receiver(post_delete, sender=ValidationRequestAttachment)
|
||||
|
@ -273,7 +246,7 @@ def auto_delete_image_files(sender, instance, **kwargs):
|
|||
if instance.image:
|
||||
try:
|
||||
delete(instance.image)
|
||||
except IOError:
|
||||
except OSError:
|
||||
pass
|
||||
if os.path.isfile(instance.image.path):
|
||||
instance.image.delete(save=False)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# authentic2_cut - Authentic2 plugin for CUT
|
||||
# Copyright (C) 2017 Entr'ouvert
|
||||
#
|
||||
|
@ -15,9 +14,8 @@
|
|||
# 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.utils.translation import ugettext_lazy as _
|
||||
|
||||
import django_tables2 as tables
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from . import models
|
||||
|
||||
|
@ -40,26 +38,23 @@ class UserModificationsTable(tables.Table):
|
|||
|
||||
class ValidationTable(tables.Table):
|
||||
pk = tables.LinkColumn(
|
||||
viewname='cut-manager-user-validation',
|
||||
kwargs={'pk': tables.A('pk')},
|
||||
verbose_name='Identifiant')
|
||||
viewname='cut-manager-user-validation', kwargs={'pk': tables.A('pk')}, verbose_name='Identifiant'
|
||||
)
|
||||
user = tables.LinkColumn(
|
||||
viewname='a2-manager-user-detail',
|
||||
kwargs={'pk': tables.A('user.pk')},
|
||||
accessor='user.get_full_name',
|
||||
order_by=('user__last_name', 'user__first_name'))
|
||||
origin = tables.Column(
|
||||
verbose_name='Service demandeur',
|
||||
orderable=False)
|
||||
human_status = tables.Column(
|
||||
order_by=('status',),
|
||||
verbose_name='Statut')
|
||||
order_by=('user__last_name', 'user__first_name'),
|
||||
)
|
||||
origin = tables.Column(verbose_name='Service demandeur', orderable=False)
|
||||
human_status = tables.Column(order_by=('status',), verbose_name='Statut')
|
||||
human_status_who = tables.LinkColumn(
|
||||
verbose_name='Agent',
|
||||
viewname='a2-manager-user-detail',
|
||||
accessor='human_status_who.get_full_name',
|
||||
kwargs={'pk': tables.A('human_status_who.pk')},
|
||||
orderable=False)
|
||||
orderable=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = models.ValidationRequest
|
||||
|
|
|
@ -14,41 +14,55 @@
|
|||
# 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.conf.urls import url
|
||||
from authentic2.manager.urls import manager_login_required
|
||||
|
||||
from . import views, api_views
|
||||
from authentic2.decorators import required
|
||||
from authentic2.manager.urls import manager_login_required
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import api_views, views
|
||||
|
||||
urlpatterns = required(
|
||||
manager_login_required, [
|
||||
manager_login_required,
|
||||
[
|
||||
url('^accounts/edit-core/$', views.edit_core, name='cut-edit-core'),
|
||||
url('^accounts/edit-crown/$', views.edit_crown, name='cut-edit-crown'),
|
||||
url(r'^manage/users/(?P<pk>\d+)/$', views.manager_user_detail,
|
||||
name='a2-manager-user-detail'),
|
||||
url(r'^manage/users/uuid:(?P<slug>[a-z0-9]+)/$', views.manager_user_detail,
|
||||
name='a2-manager-user-by-uuid-detail'),
|
||||
url('^manage/users/(?P<pk>\d+)/edit-core/$', views.manager_user_edit_core,
|
||||
name='cut-manager-user-edit-core'),
|
||||
url('^manage/users/(?P<pk>\d+)/actions-journal/$', views.user_actions_journal,
|
||||
name='cut-manager-user-actions-journal'),
|
||||
url('^manage/users/(?P<pk>\d+)/modifications-journal/$', views.user_modifications_journal,
|
||||
name='cut-manager-user-modifications-journal'),
|
||||
url('^manage/validation/$', views.validation_homepage,
|
||||
name='cut-manager-user-validation'),
|
||||
url('^manage/validation/next/$', views.next_validation,
|
||||
name='cut-manager-user-next-validation'),
|
||||
url('^manage/validation/(?P<pk>\d+)/$', views.validation,
|
||||
name='cut-manager-user-validation'),
|
||||
url('^manage/validation/attachment/(?P<pk>\d*)/(?P<filename>.*)$', views.validation_attachment,
|
||||
name='cut-manager-user-validation-attachment'),
|
||||
url('^manage/validation/attachment-thumbnail/(?P<pk>\d*)/(?P<filename>.*)$', views.validation_attachment_thumbnail,
|
||||
name='cut-manager-user-validation-attachment-thumbnail'),
|
||||
]
|
||||
url(r'^manage/users/(?P<pk>\d+)/$', views.manager_user_detail, name='a2-manager-user-detail'),
|
||||
url(
|
||||
r'^manage/users/uuid:(?P<slug>[a-z0-9]+)/$',
|
||||
views.manager_user_detail,
|
||||
name='a2-manager-user-by-uuid-detail',
|
||||
),
|
||||
url(
|
||||
r'^manage/users/(?P<pk>\d+)/edit-core/$',
|
||||
views.manager_user_edit_core,
|
||||
name='cut-manager-user-edit-core',
|
||||
),
|
||||
url(
|
||||
r'^manage/users/(?P<pk>\d+)/actions-journal/$',
|
||||
views.user_actions_journal,
|
||||
name='cut-manager-user-actions-journal',
|
||||
),
|
||||
url(
|
||||
r'^manage/users/(?P<pk>\d+)/modifications-journal/$',
|
||||
views.user_modifications_journal,
|
||||
name='cut-manager-user-modifications-journal',
|
||||
),
|
||||
url('^manage/validation/$', views.validation_homepage, name='cut-manager-user-validation'),
|
||||
url('^manage/validation/next/$', views.next_validation, name='cut-manager-user-next-validation'),
|
||||
url(r'^manage/validation/(?P<pk>\d+)/$', views.validation, name='cut-manager-user-validation'),
|
||||
url(
|
||||
r'^manage/validation/attachment/(?P<pk>\d*)/(?P<filename>.*)$',
|
||||
views.validation_attachment,
|
||||
name='cut-manager-user-validation-attachment',
|
||||
),
|
||||
url(
|
||||
r'^manage/validation/attachment-thumbnail/(?P<pk>\d*)/(?P<filename>.*)$',
|
||||
views.validation_attachment_thumbnail,
|
||||
name='cut-manager-user-validation-attachment-thumbnail',
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
urlpatterns += [
|
||||
url('^cgu/$', views.cgu,
|
||||
name='cut-cgu'),
|
||||
url('^cgu/$', views.cgu, name='cut-cgu'),
|
||||
url('^api/validate/$', api_views.validate, name='api-cut-validate'),
|
||||
]
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# authentic2_cut - Authentic2 plugin for CUT
|
||||
# Copyright (C) 2016 Entr'ouvert
|
||||
#
|
||||
|
@ -18,20 +17,21 @@
|
|||
from django.utils.html import format_html
|
||||
|
||||
|
||||
class FranceConnectUserData(object):
|
||||
class FranceConnectUserData:
|
||||
def __init__(self, user, request):
|
||||
self.user = user
|
||||
self.request = request
|
||||
|
||||
def __str__(self):
|
||||
from authentic2_auth_fc.models import FcAccount
|
||||
|
||||
if FcAccount.objects.filter(user=self.user).exists():
|
||||
return format_html(u'<p>Utilisateur relié à un compte FranceConnect</p>')
|
||||
return format_html('<p>Utilisateur relié à un compte FranceConnect</p>')
|
||||
else:
|
||||
return u''
|
||||
return ''
|
||||
|
||||
|
||||
class ValidationUserData(object):
|
||||
class ValidationUserData:
|
||||
def __init__(self, user):
|
||||
self.user = user
|
||||
|
||||
|
@ -45,8 +45,9 @@ class ValidationUserData(object):
|
|||
validation_context = self.user.attributes.validation_context
|
||||
validation_date = self.user.attributes.validation_date
|
||||
if not validation_date:
|
||||
return u''
|
||||
return format_html(u'<p id="a2-manager-user-cut-validation">Compte validé <em>{0}</em> le '
|
||||
u'{1}</p>',
|
||||
context_map.get(validation_context, validation_context),
|
||||
validation_date.strftime('%d/%m/%Y'))
|
||||
return ''
|
||||
return format_html(
|
||||
'<p id="a2-manager-user-cut-validation">Compte validé <em>{0}</em> le ' '{1}</p>',
|
||||
context_map.get(validation_context, validation_context),
|
||||
validation_date.strftime('%d/%m/%Y'),
|
||||
)
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from authentic2.a2_rbac.models import ACTIVATE_OP, RESET_PASSWORD_OP
|
||||
from django.contrib.auth import get_user_model
|
||||
from django_rbac.utils import get_ou_model, get_role_model, get_operation, get_permission_model
|
||||
from django_rbac.models import CHANGE_OP, SEARCH_OP, ADD_OP, VIEW_OP, DELETE_OP, ADMIN_OP, Operation
|
||||
from authentic2.a2_rbac.models import RESET_PASSWORD_OP, ACTIVATE_OP
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django_rbac.models import ADD_OP, ADMIN_OP, CHANGE_OP, DELETE_OP, SEARCH_OP, VIEW_OP, Operation
|
||||
from django_rbac.utils import get_operation, get_ou_model, get_permission_model, get_role_model
|
||||
|
||||
OU = get_ou_model()
|
||||
Role = get_role_model()
|
||||
|
@ -17,70 +15,70 @@ CUT_FC_OP = Operation.register(name='Modifier liaison FC', slug='cut_fc')
|
|||
|
||||
ROLE_TEMPLATES = [
|
||||
{
|
||||
'name': u'Administrateur CUT - création',
|
||||
'name': 'Administrateur CUT - création',
|
||||
'slug': '_a2-cut-create',
|
||||
'operations': [ADD_OP],
|
||||
'target': 'user_ct',
|
||||
'scope': 'ou_usagers',
|
||||
},
|
||||
{
|
||||
'name': u'Administrateur CUT - recherche',
|
||||
'name': 'Administrateur CUT - recherche',
|
||||
'slug': '_a2-cut-search',
|
||||
'operations': [SEARCH_OP],
|
||||
'target': 'user_ct',
|
||||
'scope': 'ou_usagers',
|
||||
},
|
||||
{
|
||||
'name': u'Administrateur CUT - lecture',
|
||||
'name': 'Administrateur CUT - lecture',
|
||||
'slug': '_a2-cut-view',
|
||||
'operations': [VIEW_OP],
|
||||
'target': 'user_ct',
|
||||
'scope': 'ou_usagers',
|
||||
},
|
||||
{
|
||||
'name': u'Administrateur CUT - modification',
|
||||
'name': 'Administrateur CUT - modification',
|
||||
'slug': '_a2-cut-change',
|
||||
'operations': [CHANGE_OP],
|
||||
'target': 'user_ct',
|
||||
'scope': 'ou_usagers',
|
||||
},
|
||||
{
|
||||
'name': u'Administrateur CUT - suppression',
|
||||
'name': 'Administrateur CUT - suppression',
|
||||
'slug': '_a2-cut-delete',
|
||||
'operations': [DELETE_OP],
|
||||
'target': 'user_ct',
|
||||
'scope': 'ou_usagers',
|
||||
},
|
||||
{
|
||||
'name': u'Administrateur CUT - mot de passe',
|
||||
'name': 'Administrateur CUT - mot de passe',
|
||||
'slug': '_a2-cut-passwords',
|
||||
'operations': [RESET_PASSWORD_OP],
|
||||
'target': 'user_ct',
|
||||
'scope': 'ou_usagers',
|
||||
},
|
||||
{
|
||||
'name': u'Administrateur CUT - suspension',
|
||||
'name': 'Administrateur CUT - suspension',
|
||||
'slug': '_a2-cut-activate',
|
||||
'operations': [ACTIVATE_OP],
|
||||
'target': 'user_ct',
|
||||
'scope': 'ou_usagers',
|
||||
},
|
||||
{
|
||||
'name': u'Administrateur CUT - validation',
|
||||
'name': 'Administrateur CUT - validation',
|
||||
'slug': '_a2-cut-validate',
|
||||
'operations': [CUT_VALIDATE_OP],
|
||||
'target': 'user_ct',
|
||||
'scope': 'ou_usagers',
|
||||
},
|
||||
{
|
||||
'name': u'Administrateur CUT - fc',
|
||||
'name': 'Administrateur CUT - fc',
|
||||
'slug': '_a2-cut-fc',
|
||||
'operations': [CUT_FC_OP, SEARCH_OP, VIEW_OP],
|
||||
'target': 'user_ct',
|
||||
'scope': 'ou_usagers',
|
||||
},
|
||||
{
|
||||
'name': u'Administrateur CUT',
|
||||
'name': 'Administrateur CUT',
|
||||
'slug': '_a2-cut-admin-usagers',
|
||||
'role_parents': [
|
||||
'_a2-cut-create',
|
||||
|
@ -92,10 +90,10 @@ ROLE_TEMPLATES = [
|
|||
'_a2-cut-activate',
|
||||
'_a2-cut-validate',
|
||||
'_a2-cut-fc',
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': u'Utilisateurs',
|
||||
'name': 'Utilisateurs',
|
||||
'slug': '_a2-cut-admin-users',
|
||||
'operations': [ADMIN_OP],
|
||||
'target': 'user_ct',
|
||||
|
@ -103,7 +101,7 @@ ROLE_TEMPLATES = [
|
|||
'child_of_ou': ['ou_territoire', '__prefix__'],
|
||||
},
|
||||
{
|
||||
'name': u'Rôles',
|
||||
'name': 'Rôles',
|
||||
'slug': '_a2-cut-admin-roles',
|
||||
'operations': [ADMIN_OP],
|
||||
'target': 'role_ct',
|
||||
|
@ -111,7 +109,7 @@ ROLE_TEMPLATES = [
|
|||
'child_of_ou': ['ou_territoire', '__prefix__'],
|
||||
},
|
||||
{
|
||||
'name': u'Administrateur lecteur',
|
||||
'name': 'Administrateur lecteur',
|
||||
'slug': '_a2-cut-admin-view-only',
|
||||
'operations': [VIEW_OP],
|
||||
'target': 'user_ct',
|
||||
|
@ -119,12 +117,9 @@ ROLE_TEMPLATES = [
|
|||
'child_of_ou': ['ou_territoire', '__prefix__'],
|
||||
},
|
||||
{
|
||||
'name': u'Administrateur',
|
||||
'name': 'Administrateur',
|
||||
'slug': '_a2-cut-admin',
|
||||
'role_parents': [
|
||||
'_a2-cut-admin-users',
|
||||
'_a2-cut-admin-roles'
|
||||
],
|
||||
'role_parents': ['_a2-cut-admin-users', '_a2-cut-admin-roles'],
|
||||
'scope': 'no_scope',
|
||||
},
|
||||
]
|
||||
|
@ -141,22 +136,18 @@ def update_roles():
|
|||
default_ou.save()
|
||||
|
||||
ou_usagers, created = OU.objects.get_or_create(
|
||||
slug='usagers',
|
||||
defaults={
|
||||
'name': u'Usagers',
|
||||
'default': True
|
||||
}
|
||||
slug='usagers', defaults={'name': 'Usagers', 'default': True}
|
||||
)
|
||||
if ou_usagers.default is False:
|
||||
ou_usagers.default = True
|
||||
ou_usagers.save()
|
||||
|
||||
ou_territoire, created = OU.objects.get_or_create(
|
||||
name=u'Territoire',
|
||||
name='Territoire',
|
||||
slug='territoire',
|
||||
defaults={
|
||||
'name': u'Territoire',
|
||||
}
|
||||
'name': 'Territoire',
|
||||
},
|
||||
)
|
||||
|
||||
ct_ct = ContentType.objects.get_for_model(ContentType)
|
||||
|
@ -174,11 +165,7 @@ def update_roles():
|
|||
return
|
||||
for tpl in ROLE_TEMPLATES:
|
||||
role, created = Role.objects.get_or_create(
|
||||
slug=tpl['slug'],
|
||||
ou=ou,
|
||||
defaults={
|
||||
'name': tpl['name']
|
||||
}
|
||||
slug=tpl['slug'], ou=ou, defaults={'name': tpl['name']}
|
||||
)
|
||||
roles[(ou, tpl['slug'])] = role
|
||||
if tpl.get('operations'):
|
||||
|
@ -191,16 +178,13 @@ def update_roles():
|
|||
for operation in tpl['operations']:
|
||||
op = get_operation(operation)
|
||||
permission, created = Permission.objects.get_or_create(
|
||||
operation=op,
|
||||
ou=scope,
|
||||
target_ct=ct_ct,
|
||||
target_id=target_ct.pk)
|
||||
operation=op, ou=scope, target_ct=ct_ct, target_id=target_ct.pk
|
||||
)
|
||||
permissions.append(permission)
|
||||
op = get_operation(VIEW_OP)
|
||||
permission, created = Permission.objects.get_or_create(
|
||||
operation=op,
|
||||
target_ct=ou_ct,
|
||||
target_id=scope.pk)
|
||||
operation=op, target_ct=ou_ct, target_id=scope.pk
|
||||
)
|
||||
permissions.append(permission)
|
||||
role.permissions.set(permissions)
|
||||
else:
|
||||
|
@ -219,9 +203,7 @@ def update_roles():
|
|||
elif ou.slug != child:
|
||||
child_ou = vars()[child]
|
||||
if child_ou:
|
||||
child_role = Role.objects.get(
|
||||
ou=child_ou,
|
||||
slug=tpl['slug'])
|
||||
child_role = Role.objects.get(ou=child_ou, slug=tpl['slug'])
|
||||
role.add_child(child_role)
|
||||
if 'role_parents' in tpl:
|
||||
for role_parent in tpl['role_parents']:
|
||||
|
@ -230,12 +212,12 @@ def update_roles():
|
|||
if tpl['name'] == 'Administrateur' and ou.slug == 'territoire':
|
||||
op = get_operation(ADMIN_OP)
|
||||
permission, created = Permission.objects.get_or_create(
|
||||
operation=op,
|
||||
target_ct=ct_ct,
|
||||
target_id=ou_ct.pk)
|
||||
operation=op, target_ct=ct_ct, target_id=ou_ct.pk
|
||||
)
|
||||
role.permissions.add(permission)
|
||||
slugs = [tpl['slug'] for tpl in ROLE_TEMPLATES]
|
||||
Role.objects.filter(slug__startswith='_a2-cut').exclude(slug__in=slugs).delete()
|
||||
|
||||
handle_ou(ou_territoire, **vars())
|
||||
for ou in OU.objects.exclude(slug__in=['usagers', 'territoire']):
|
||||
handle_ou(**vars())
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# authentic2_cut - Authentic2 plugin for CUT
|
||||
# Copyright (C) 2017 Entr'ouvert
|
||||
#
|
||||
|
@ -17,23 +16,22 @@
|
|||
|
||||
from contextlib import closing
|
||||
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from authentic2 import hooks
|
||||
from authentic2.manager.user_views import UserDetailView, UserEditView
|
||||
from authentic2.manager.views import BaseTableView, FilterQuerysetByPermMixin, SimpleSubTableView
|
||||
from authentic2.utils.misc import redirect
|
||||
from authentic2.views import EditProfile
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import HttpResponse, HttpResponseRedirect, Http404
|
||||
from django.db.transaction import atomic
|
||||
from django.http import Http404, HttpResponse, HttpResponseRedirect
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic.base import TemplateView
|
||||
|
||||
from authentic2.views import EditProfile
|
||||
from authentic2.manager.views import SimpleSubTableView, BaseTableView, FilterQuerysetByPermMixin
|
||||
from authentic2.manager.user_views import UserEditView, UserDetailView
|
||||
from authentic2 import hooks
|
||||
from authentic2.utils.misc import redirect
|
||||
|
||||
from . import forms, models, tables, utils
|
||||
from .custom_settings import CORE_ATTRIBUTES
|
||||
from . import models, forms, tables, utils
|
||||
|
||||
|
||||
class EditCoreView(EditProfile):
|
||||
|
@ -49,14 +47,15 @@ class EditCoreView(EditProfile):
|
|||
|
||||
@classmethod
|
||||
def get_fields(cls, scopes=None):
|
||||
fields, labels = super(EditCoreView, cls).get_fields(scopes=scopes)
|
||||
fields, labels = super().get_fields(scopes=scopes)
|
||||
return [field for field in fields if field in cls.fields], labels
|
||||
|
||||
def form_valid(self, form):
|
||||
response = super(EditCoreView, self).form_valid(form)
|
||||
response = super().form_valid(form)
|
||||
hooks.call_hooks('event', name='cut-edit-core', user=self.request.user, form=form)
|
||||
return response
|
||||
|
||||
|
||||
edit_core = EditCoreView.as_view()
|
||||
|
||||
|
||||
|
@ -73,14 +72,15 @@ class EditCrownView(EditProfile):
|
|||
|
||||
@classmethod
|
||||
def get_fields(cls, scopes=None):
|
||||
fields, labels = super(EditCrownView, cls).get_fields(scopes=scopes)
|
||||
fields, labels = super().get_fields(scopes=scopes)
|
||||
return [field for field in fields if field not in cls.fields], labels
|
||||
|
||||
def form_valid(self, form):
|
||||
response = super(EditCrownView, self).form_valid(form)
|
||||
response = super().form_valid(form)
|
||||
hooks.call_hooks('event', name='cut-edit-crown', user=self.request.user, form=form)
|
||||
return response
|
||||
|
||||
|
||||
edit_crown = EditCrownView.as_view()
|
||||
|
||||
|
||||
|
@ -90,21 +90,21 @@ class UserEditCoreView(UserEditView):
|
|||
|
||||
def get_title(self):
|
||||
if self.object.attributes.validated:
|
||||
return u'Modifier les attributs cœurs'
|
||||
return u'Valider les attributs cœurs'
|
||||
return 'Modifier les attributs cœurs'
|
||||
return 'Valider les attributs cœurs'
|
||||
|
||||
def get_fields(self):
|
||||
fields = super(UserEditCoreView, self).get_fields()
|
||||
fields = super().get_fields()
|
||||
return [field for field in CORE_ATTRIBUTES if field in fields]
|
||||
|
||||
def get_form(self, *args, **kwargs):
|
||||
form = super(UserEditCoreView, self).get_form(*args, **kwargs)
|
||||
form = super().get_form(*args, **kwargs)
|
||||
for field in form.fields.values():
|
||||
field.required = True
|
||||
return form
|
||||
|
||||
def form_valid(self, form, context='office', partner=''):
|
||||
response = super(UserEditCoreView, self).form_valid(form)
|
||||
response = super().form_valid(form)
|
||||
already_validated = form.instance.attributes.validated
|
||||
if form.has_changed() or not already_validated:
|
||||
form.instance.attributes.validated = True
|
||||
|
@ -113,28 +113,42 @@ class UserEditCoreView(UserEditView):
|
|||
if not partner and self.request.user.ou:
|
||||
partner = self.request.user.ou.slug
|
||||
form.instance.attributes.validation_partner = partner
|
||||
hooks.call_hooks('event', user=self.request.user, name='manager-cut-validate',
|
||||
instance=form.instance, form=form, context=context, partner=partner)
|
||||
hooks.call_hooks(
|
||||
'event',
|
||||
user=self.request.user,
|
||||
name='manager-cut-validate',
|
||||
instance=form.instance,
|
||||
form=form,
|
||||
context=context,
|
||||
partner=partner,
|
||||
)
|
||||
if already_validated:
|
||||
msg = u'Les données cœur ont été modifiées.'
|
||||
msg = 'Les données cœur ont été modifiées.'
|
||||
else:
|
||||
msg = u'Le compte a été validé.'
|
||||
msg = 'Le compte a été validé.'
|
||||
messages.info(self.request, msg)
|
||||
return response
|
||||
|
||||
|
||||
manager_user_edit_core = UserEditCoreView.as_view()
|
||||
|
||||
|
||||
class ManagerUserDetailView(UserDetailView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
response = super(ManagerUserDetailView, self).get(request, *args, **kwargs)
|
||||
response = super().get(request, *args, **kwargs)
|
||||
# journalise les accès aux fiches une fois par heure et par session
|
||||
t = now()
|
||||
key = 'user-looked-%s-%s-%s' % (self.object, t.date(), t.time().hour)
|
||||
if key not in request.session:
|
||||
request.session[key] = True
|
||||
hooks.call_hooks('event', name='manager-view-user', user=request.user,
|
||||
instance=self.object, request=request, **kwargs)
|
||||
hooks.call_hooks(
|
||||
'event',
|
||||
name='manager-view-user',
|
||||
user=request.user,
|
||||
instance=self.object,
|
||||
request=request,
|
||||
**kwargs,
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
|
@ -151,6 +165,7 @@ class UserActionsJournal(SimpleSubTableView):
|
|||
def get_table_queryset(self):
|
||||
return self.object.actor_journal.all()
|
||||
|
||||
|
||||
user_actions_journal = UserActionsJournal.as_view()
|
||||
|
||||
|
||||
|
@ -164,6 +179,7 @@ class UserModificationsJournal(SimpleSubTableView):
|
|||
def get_table_queryset(self):
|
||||
return self.object.subject_journal.all()
|
||||
|
||||
|
||||
user_modifications_journal = UserModificationsJournal.as_view()
|
||||
|
||||
|
||||
|
@ -172,7 +188,8 @@ class CGU(TemplateView):
|
|||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['cut_base_url'] = self.request.build_absolute_uri('/')
|
||||
return super(CGU, self).get_context_data(**kwargs)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
cgu = CGU.as_view()
|
||||
|
||||
|
@ -183,10 +200,9 @@ def next_validation(request):
|
|||
with atomic():
|
||||
validation_request = models.ValidationRequest.objects.next_request(request.user)
|
||||
if not validation_request:
|
||||
messages.info(request, u'Il n\'y aucune demande de validation en ce moment.')
|
||||
messages.info(request, 'Il n\'y aucune demande de validation en ce moment.')
|
||||
return redirect(request, 'a2-manager-homepage')
|
||||
return redirect(request, 'cut-manager-user-validation',
|
||||
kwargs={'pk': validation_request.pk})
|
||||
return redirect(request, 'cut-manager-user-validation', kwargs={'pk': validation_request.pk})
|
||||
|
||||
|
||||
class Validation(UserEditCoreView):
|
||||
|
@ -195,21 +211,21 @@ class Validation(UserEditCoreView):
|
|||
|
||||
def get_object(self, queryset=None):
|
||||
qs = models.ValidationRequest.objects.all()
|
||||
self.validation_request = super(Validation, self).get_object(queryset=qs)
|
||||
self.validation_request = super().get_object(queryset=qs)
|
||||
return self.validation_request.user
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
# si la demande en cours a déjà été traitée, on passe à la suivante
|
||||
try:
|
||||
return super(Validation, self).dispatch(request, *args, **kwargs)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
except Http404:
|
||||
return redirect(request, 'cut-manager-user-next-validation')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super(Validation, self).get_context_data(**kwargs)
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['validation_request'] = self.validation_request
|
||||
ctx['attachments'] = self.validation_request.attachments.all()
|
||||
ctx['action'] = u'Valider'
|
||||
ctx['action'] = 'Valider'
|
||||
ctx['validation_form'] = forms.ValidationForm()
|
||||
return ctx
|
||||
|
||||
|
@ -221,9 +237,9 @@ class Validation(UserEditCoreView):
|
|||
if 'refuse' in request.POST:
|
||||
reason = request.POST.get('reason')
|
||||
if not reason:
|
||||
messages.error(request, u'Vous devez préciser une raison pour le refus.')
|
||||
messages.error(request, 'Vous devez préciser une raison pour le refus.')
|
||||
return HttpResponseRedirect('')
|
||||
messages.info(request, u'Demande traitée.')
|
||||
messages.info(request, 'Demande traitée.')
|
||||
self.validation_request.reason = reason
|
||||
self.validation_request.status = models.ValidationRequest.STATUS_REFUSED
|
||||
self.validation_request.validated = now()
|
||||
|
@ -231,7 +247,7 @@ class Validation(UserEditCoreView):
|
|||
self.validation_request.save()
|
||||
return self.next_request(request)
|
||||
elif 'validate' in request.POST:
|
||||
return super(Validation, self).post(request, *args, **kwargs)
|
||||
return super().post(request, *args, **kwargs)
|
||||
else: # next
|
||||
return self.next_request(request)
|
||||
|
||||
|
@ -240,11 +256,11 @@ class Validation(UserEditCoreView):
|
|||
# recommençons du début
|
||||
with atomic():
|
||||
validation_request = models.ValidationRequest.objects.next_request(
|
||||
request.user, after=self.validation_request)
|
||||
request.user, after=self.validation_request
|
||||
)
|
||||
if not validation_request:
|
||||
return redirect(request, 'cut-manager-user-next-validation')
|
||||
return redirect(request, 'cut-manager-user-validation',
|
||||
kwargs={'pk': validation_request.pk})
|
||||
return redirect(request, 'cut-manager-user-validation', kwargs={'pk': validation_request.pk})
|
||||
|
||||
def form_valid(self, form):
|
||||
partner = ''
|
||||
|
@ -257,12 +273,20 @@ class Validation(UserEditCoreView):
|
|||
form.instance.attributes.validation_context = 'online'
|
||||
form.instance.attributes.validation_date = now().date()
|
||||
form.instance.attributes.validation_partner = partner
|
||||
hooks.call_hooks('event', user=self.request.user, name='manager-cut-validate',
|
||||
instance=form.instance, form=form, context='office', partner=partner)
|
||||
hooks.call_hooks(
|
||||
'event',
|
||||
user=self.request.user,
|
||||
name='manager-cut-validate',
|
||||
instance=form.instance,
|
||||
form=form,
|
||||
context='office',
|
||||
partner=partner,
|
||||
)
|
||||
self.validation_request.accept(self.request.user)
|
||||
messages.info(self.request, u'Demande traitée.')
|
||||
messages.info(self.request, 'Demande traitée.')
|
||||
return self.next_request(self.request)
|
||||
|
||||
|
||||
validation = Validation.as_view()
|
||||
|
||||
|
||||
|
@ -300,4 +324,5 @@ class ValidationHomepage(BaseTableView):
|
|||
qs = qs.prefetch_related('origin').select_related()
|
||||
return qs
|
||||
|
||||
|
||||
validation_homepage = ValidationHomepage.as_view()
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import copy
|
||||
import time
|
||||
|
||||
import pytest
|
||||
import django_webtest
|
||||
from collections import namedtuple
|
||||
|
||||
import django_webtest
|
||||
import pytest
|
||||
|
||||
try:
|
||||
import pathlib
|
||||
except ImportError:
|
||||
import pathlib2 as pathlib
|
||||
|
||||
from django.core.management import call_command
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from django.core.management import call_command
|
||||
from django_rbac.utils import get_ou_model
|
||||
|
||||
User = get_user_model()
|
||||
|
@ -59,10 +59,11 @@ def glc_app(app, glc):
|
|||
return app
|
||||
|
||||
|
||||
class AllHook(object):
|
||||
class AllHook:
|
||||
def __init__(self):
|
||||
self.calls = {}
|
||||
from authentic2 import hooks
|
||||
|
||||
hooks.get_hooks.cache.clear()
|
||||
|
||||
def __call__(self, hook_name, *args, **kwargs):
|
||||
|
@ -83,7 +84,8 @@ def user(db):
|
|||
email='john.doe@example.net',
|
||||
first_name='John',
|
||||
last_name='Doe',
|
||||
email_verified=True)
|
||||
email_verified=True,
|
||||
)
|
||||
user.set_password('john.doe')
|
||||
return user
|
||||
|
||||
|
@ -102,11 +104,7 @@ def hooks(settings):
|
|||
|
||||
@pytest.fixture
|
||||
def admin(db):
|
||||
user = User(
|
||||
username='admin',
|
||||
email='admin@example.net',
|
||||
is_superuser=True,
|
||||
is_staff=True)
|
||||
user = User(username='admin', email='admin@example.net', is_superuser=True, is_staff=True)
|
||||
user.ou = OU.objects.get(slug='territoire')
|
||||
user.set_password('admin')
|
||||
user.save()
|
||||
|
@ -135,7 +133,7 @@ def pdf_file():
|
|||
def redis_client(monkeypatch):
|
||||
from authentic2_cut.apps import AppConfig
|
||||
|
||||
class RedisClientMock(object):
|
||||
class RedisClientMock:
|
||||
def __init__(self):
|
||||
self.contents = {}
|
||||
self.timeouts = {}
|
||||
|
@ -158,4 +156,5 @@ def redis_client(monkeypatch):
|
|||
@pytest.fixture(autouse=True)
|
||||
def clean_caches():
|
||||
from authentic2.apps.journal.models import event_type_cache
|
||||
|
||||
event_type_cache.cache.clear()
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.utils.encoding import force_text
|
||||
from utils import login
|
||||
|
||||
from authentic2_cut import models
|
||||
|
||||
from utils import login
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
JOHN = u'Jôhn'
|
||||
DOE = u'Dôe'
|
||||
JOHN = 'Jôhn'
|
||||
DOE = 'Dôe'
|
||||
EMAIL = 'john.doe@example.com'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def john(glc_app):
|
||||
response = glc_app.post_json('/api/users/', params={
|
||||
'first_name': JOHN,
|
||||
'last_name': DOE,
|
||||
'email': EMAIL,
|
||||
})
|
||||
response = glc_app.post_json(
|
||||
'/api/users/',
|
||||
params={
|
||||
'first_name': JOHN,
|
||||
'last_name': DOE,
|
||||
'email': EMAIL,
|
||||
},
|
||||
)
|
||||
user = User.objects.get(first_name=JOHN)
|
||||
assert response.json['sub'] != user.uuid
|
||||
assert response.json['first_name'] == JOHN
|
||||
|
@ -44,7 +44,7 @@ def test_no_email(glc_app):
|
|||
app = glc_app
|
||||
|
||||
response = app.post_json('/api/users/', params={}, status=400)
|
||||
assert set(response.json['errors']) == set(['first_name', 'last_name', 'email'])
|
||||
assert set(response.json['errors']) == {'first_name', 'last_name', 'email'}
|
||||
assert response.json['result'] == 0
|
||||
|
||||
|
||||
|
@ -54,12 +54,18 @@ def test_create_user(john):
|
|||
|
||||
def helper_test_validation_image(glc_app, john, image_file, extension):
|
||||
external_id = uuid.uuid4().hex
|
||||
response = glc_app.post_json('/api/users/%s/validate/' % john._oidc_sub, params={
|
||||
'external_id': external_id,
|
||||
'justificatifs': [{
|
||||
'b64_content': force_text(base64.b64encode(image_file)),
|
||||
}],
|
||||
}, status=201)
|
||||
response = glc_app.post_json(
|
||||
'/api/users/%s/validate/' % john._oidc_sub,
|
||||
params={
|
||||
'external_id': external_id,
|
||||
'justificatifs': [
|
||||
{
|
||||
'b64_content': force_text(base64.b64encode(image_file)),
|
||||
}
|
||||
],
|
||||
},
|
||||
status=201,
|
||||
)
|
||||
assert response.json == {
|
||||
'status': 'received',
|
||||
'id': response.json['id'],
|
||||
|
@ -97,20 +103,24 @@ def test_validation_pdf(app, admin, glc_app, john, pdf_file):
|
|||
|
||||
def test_many_attachments(app, admin, glc_app, john, png_file, jpeg_file, pdf_file):
|
||||
external_id = uuid.uuid4().hex
|
||||
response = glc_app.post_json('/api/users/%s/validate/' % john._oidc_sub, params={
|
||||
'external_id': external_id,
|
||||
'justificatifs': [
|
||||
{
|
||||
'b64_content': force_text(base64.b64encode(png_file)),
|
||||
},
|
||||
{
|
||||
'b64_content': force_text(base64.b64encode(jpeg_file)),
|
||||
},
|
||||
{
|
||||
'b64_content': force_text(base64.b64encode(pdf_file)),
|
||||
},
|
||||
],
|
||||
}, status=201)
|
||||
response = glc_app.post_json(
|
||||
'/api/users/%s/validate/' % john._oidc_sub,
|
||||
params={
|
||||
'external_id': external_id,
|
||||
'justificatifs': [
|
||||
{
|
||||
'b64_content': force_text(base64.b64encode(png_file)),
|
||||
},
|
||||
{
|
||||
'b64_content': force_text(base64.b64encode(jpeg_file)),
|
||||
},
|
||||
{
|
||||
'b64_content': force_text(base64.b64encode(pdf_file)),
|
||||
},
|
||||
],
|
||||
},
|
||||
status=201,
|
||||
)
|
||||
assert response.json == {
|
||||
'status': 'received',
|
||||
'id': response.json['id'],
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import os.path
|
||||
import datetime
|
||||
import os.path
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.management import call_command
|
||||
|
@ -8,12 +8,10 @@ from authentic2_cut import models
|
|||
|
||||
|
||||
def test_validation_request_cleanup(db, admin, user, glc, freezer, png_file):
|
||||
req = models.ValidationRequest.objects.create(
|
||||
origin=glc.oidc_client,
|
||||
user=user)
|
||||
req = models.ValidationRequest.objects.create(origin=glc.oidc_client, user=user)
|
||||
attachment = models.ValidationRequestAttachment.objects.create(
|
||||
validation_request=req,
|
||||
image=ContentFile(png_file, 'aaa'))
|
||||
validation_request=req, image=ContentFile(png_file, 'aaa')
|
||||
)
|
||||
assert models.ValidationRequest.objects.count() == 1
|
||||
assert models.ValidationRequestAttachment.objects.count() == 1
|
||||
call_command('cleanupauthentic')
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from django.urls import reverse
|
||||
|
||||
from authentic2.utils.misc import make_url
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
def login(app, user, path=None, password=None, remember_me=None):
|
||||
|
|
Loading…
Reference in New Issue