diff --git a/cof/settings/common.py b/cof/settings/common.py index ba0b6044..92759d21 100644 --- a/cof/settings/common.py +++ b/cof/settings/common.py @@ -90,6 +90,7 @@ INSTALLED_APPS = [ 'wagtailmenus', 'modelcluster', 'taggit', + 'kfet.auth', 'kfet.cms', ] @@ -99,7 +100,7 @@ MIDDLEWARE_CLASSES = [ 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', - 'kfet.middleware.KFetAuthenticationMiddleware', + 'kfet.auth.middleware.KFetAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', @@ -127,7 +128,7 @@ TEMPLATES = [ 'wagtailmenus.context_processors.wagtailmenus', 'djconfig.context_processors.config', 'gestioncof.shared.context_processor', - 'kfet.context_processors.auth', + 'kfet.auth.context_processors.auth', 'kfet.context_processors.config', ], }, @@ -190,7 +191,7 @@ CAS_EMAIL_FORMAT = "%s@clipper.ens.fr" AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'gestioncof.shared.COFCASBackend', - 'kfet.backends.GenericTeamBackend', + 'kfet.auth.backends.GenericTeamBackend', ) RECAPTCHA_USE_SSL = True diff --git a/kfet/auth/__init__.py b/kfet/auth/__init__.py new file mode 100644 index 00000000..63392684 --- /dev/null +++ b/kfet/auth/__init__.py @@ -0,0 +1 @@ +default_app_config = 'kfet.auth.apps.KFetAuthConfig' diff --git a/kfet/auth/apps.py b/kfet/auth/apps.py new file mode 100644 index 00000000..ab791d18 --- /dev/null +++ b/kfet/auth/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig +from django.utils.translation import ugettext_lazy as _ + + +class KFetAuthConfig(AppConfig): + name = 'kfet.auth' + label = 'kfetauth' + verbose_name = _("K-Fêt - Authentification et Autorisation") diff --git a/kfet/backends.py b/kfet/auth/backends.py similarity index 100% rename from kfet/backends.py rename to kfet/auth/backends.py diff --git a/kfet/auth/context_processors.py b/kfet/auth/context_processors.py new file mode 100644 index 00000000..07c9537f --- /dev/null +++ b/kfet/auth/context_processors.py @@ -0,0 +1,10 @@ +from django.contrib.auth.context_processors import PermWrapper + + +def auth(request): + if hasattr(request, 'real_user'): + return { + 'user': request.real_user, + 'perms': PermWrapper(request.real_user), + } + return {} diff --git a/kfet/auth/fields.py b/kfet/auth/fields.py new file mode 100644 index 00000000..28ba1c9e --- /dev/null +++ b/kfet/auth/fields.py @@ -0,0 +1,20 @@ +from django import forms +from django.contrib.auth.models import Permission +from django.contrib.contenttypes.models import ContentType +from django.forms import widgets + + +class KFetPermissionsField(forms.ModelMultipleChoiceField): + + def __init__(self, *args, **kwargs): + queryset = Permission.objects.filter( + content_type__in=ContentType.objects.filter(app_label="kfet"), + ) + super().__init__( + queryset=queryset, + widget=widgets.CheckboxSelectMultiple, + *args, **kwargs + ) + + def label_from_instance(self, obj): + return obj.name diff --git a/kfet/auth/forms.py b/kfet/auth/forms.py new file mode 100644 index 00000000..0c9fa53b --- /dev/null +++ b/kfet/auth/forms.py @@ -0,0 +1,44 @@ +from django import forms +from django.contrib.auth.models import Group, User + +from .fields import KFetPermissionsField + + +class GroupForm(forms.ModelForm): + permissions = KFetPermissionsField() + + def clean_name(self): + name = self.cleaned_data['name'] + return 'K-Fêt %s' % name + + def clean_permissions(self): + kfet_perms = self.cleaned_data['permissions'] + # TODO: With Django >=1.11, the QuerySet method 'difference' can be + # used. + # other_groups = self.instance.permissions.difference( + # self.fields['permissions'].queryset + # ) + other_perms = self.instance.permissions.exclude( + pk__in=[p.pk for p in self.fields['permissions'].queryset], + ) + return list(kfet_perms) + list(other_perms) + + class Meta: + model = Group + fields = ['name', 'permissions'] + + +class UserGroupForm(forms.ModelForm): + groups = forms.ModelMultipleChoiceField( + Group.objects.filter(name__icontains='K-Fêt'), + label='Statut équipe', + required=False) + + def clean_groups(self): + kfet_groups = self.cleaned_data.get('groups') + other_groups = self.instance.groups.exclude(name__icontains='K-Fêt') + return list(kfet_groups) + list(other_groups) + + class Meta: + model = User + fields = ['groups'] diff --git a/kfet/middleware.py b/kfet/auth/middleware.py similarity index 95% rename from kfet/middleware.py rename to kfet/auth/middleware.py index 9502d393..1a930c3b 100644 --- a/kfet/middleware.py +++ b/kfet/auth/middleware.py @@ -2,7 +2,7 @@ from django.contrib.auth.models import User -from kfet.backends import KFetBackend +from .backends import KFetBackend class KFetAuthenticationMiddleware(object): diff --git a/kfet/auth/migrations/0001_initial.py b/kfet/auth/migrations/0001_initial.py new file mode 100644 index 00000000..30dfca70 --- /dev/null +++ b/kfet/auth/migrations/0001_initial.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0006_require_contenttypes_0002'), + ] + + operations = [ + migrations.CreateModel( + name='GenericTeamToken', + fields=[ + ('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)), + ('token', models.CharField(unique=True, max_length=50)), + ], + ), + ] diff --git a/kfet/auth/migrations/__init__.py b/kfet/auth/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/kfet/auth/models.py b/kfet/auth/models.py new file mode 100644 index 00000000..53aef6c9 --- /dev/null +++ b/kfet/auth/models.py @@ -0,0 +1,5 @@ +from django.db import models + + +class GenericTeamToken(models.Model): + token = models.CharField(max_length=50, unique=True) diff --git a/kfet/templates/kfet/login_genericteam.html b/kfet/auth/templates/kfet/login_genericteam.html similarity index 100% rename from kfet/templates/kfet/login_genericteam.html rename to kfet/auth/templates/kfet/login_genericteam.html diff --git a/kfet/tests/test_forms.py b/kfet/auth/tests.py similarity index 100% rename from kfet/tests/test_forms.py rename to kfet/auth/tests.py diff --git a/kfet/auth/views.py b/kfet/auth/views.py new file mode 100644 index 00000000..ce44b007 --- /dev/null +++ b/kfet/auth/views.py @@ -0,0 +1,69 @@ +from django.contrib import messages +from django.contrib.messages.views import SuccessMessageMixin +from django.contrib.auth import authenticate, login +from django.contrib.auth.decorators import permission_required +from django.contrib.auth.models import Group, User +from django.core.urlresolvers import reverse_lazy +from django.db.models import Prefetch +from django.shortcuts import render +from django.utils.crypto import get_random_string +from django.views.generic.edit import CreateView, UpdateView + +from django_cas_ng.views import logout as cas_logout_view + +from kfet.decorators import teamkfet_required + +from .forms import GroupForm +from .models import GenericTeamToken + + +@teamkfet_required +def login_genericteam(request): + # Check si besoin de déconnecter l'utilisateur de CAS + cas_logout = None + if request.user.profile.login_clipper: + # Récupèration de la vue de déconnexion de CAS + # Ici, car request sera modifié après + next_page = request.META.get('HTTP_REFERER', None) + cas_logout = cas_logout_view(request, next_page=next_page) + + # Authentification du compte générique + token = GenericTeamToken.objects.create(token=get_random_string(50)) + user = authenticate(username="kfet_genericteam", token=token.token) + login(request, user) + + messages.success(request, "Connecté en utilisateur partagé") + + return cas_logout or render(request, "kfet/login_genericteam.html") + + +@permission_required('kfet.manage_perms') +def account_group(request): + user_pre = Prefetch( + 'user_set', + queryset=User.objects.select_related('profile__account_kfet'), + ) + groups = ( + Group.objects + .filter(name__icontains='K-Fêt') + .prefetch_related('permissions', user_pre) + ) + return render(request, 'kfet/account_group.html', { + 'groups': groups, + }) + + +class AccountGroupCreate(SuccessMessageMixin, CreateView): + model = Group + template_name = 'kfet/account_group_form.html' + form_class = GroupForm + success_message = 'Nouveau groupe : %(name)s' + success_url = reverse_lazy('kfet.account.group') + + +class AccountGroupUpdate(SuccessMessageMixin, UpdateView): + queryset = Group.objects.filter(name__icontains='K-Fêt') + template_name = 'kfet/account_group_form.html' + form_class = GroupForm + success_message = 'Groupe modifié : %(name)s' + success_url = reverse_lazy('kfet.account.group') diff --git a/kfet/context_processors.py b/kfet/context_processors.py index 4c7b4fe4..04feec81 100644 --- a/kfet/context_processors.py +++ b/kfet/context_processors.py @@ -1,18 +1,7 @@ # -*- coding: utf-8 -*- -from django.contrib.auth.context_processors import PermWrapper - from kfet.config import kfet_config -def auth(request): - if hasattr(request, 'real_user'): - return { - 'user': request.real_user, - 'perms': PermWrapper(request.real_user), - } - return {} - - def config(request): return {'kfet_config': kfet_config} diff --git a/kfet/forms.py b/kfet/forms.py index 6ef3aefb..66638e6c 100644 --- a/kfet/forms.py +++ b/kfet/forms.py @@ -5,9 +5,8 @@ from decimal import Decimal from django import forms from django.core.exceptions import ValidationError -from django.contrib.auth.models import User, Group, Permission -from django.contrib.contenttypes.models import ContentType -from django.forms import modelformset_factory, widgets +from django.contrib.auth.models import User +from django.forms import modelformset_factory from django.utils import timezone from djconfig.forms import ConfigForm @@ -18,6 +17,8 @@ from kfet.models import ( TransferGroup, Supplier) from gestioncof.models import CofProfile +from .auth.forms import UserGroupForm # noqa + # ----- # Widgets @@ -128,60 +129,6 @@ class UserRestrictTeamForm(UserForm): fields = ['first_name', 'last_name', 'email'] -class UserGroupForm(forms.ModelForm): - groups = forms.ModelMultipleChoiceField( - Group.objects.filter(name__icontains='K-Fêt'), - label='Statut équipe', - required=False) - - def clean_groups(self): - kfet_groups = self.cleaned_data.get('groups') - other_groups = self.instance.groups.exclude(name__icontains='K-Fêt') - return list(kfet_groups) + list(other_groups) - - class Meta: - model = User - fields = ['groups'] - - -class KFetPermissionsField(forms.ModelMultipleChoiceField): - - def __init__(self, *args, **kwargs): - queryset = Permission.objects.filter( - content_type__in=ContentType.objects.filter(app_label="kfet"), - ) - super().__init__( - queryset=queryset, - widget=widgets.CheckboxSelectMultiple, - *args, **kwargs - ) - - def label_from_instance(self, obj): - return obj.name - - -class GroupForm(forms.ModelForm): - permissions = KFetPermissionsField() - - def clean_name(self): - name = self.cleaned_data['name'] - return 'K-Fêt %s' % name - - def clean_permissions(self): - kfet_perms = self.cleaned_data['permissions'] - # TODO: With Django >=1.11, the QuerySet method 'difference' can be used. - # other_groups = self.instance.permissions.difference( - # self.fields['permissions'].queryset - # ) - other_perms = self.instance.permissions.exclude( - pk__in=[p.pk for p in self.fields['permissions'].queryset], - ) - return list(kfet_perms) + list(other_perms) - - class Meta: - model = Group - fields = ['name', 'permissions'] - class AccountNegativeForm(forms.ModelForm): class Meta: diff --git a/kfet/migrations/0058_delete_genericteamtoken.py b/kfet/migrations/0058_delete_genericteamtoken.py new file mode 100644 index 00000000..ea8b55cd --- /dev/null +++ b/kfet/migrations/0058_delete_genericteamtoken.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('kfet', '0057_merge'), + ] + + operations = [ + migrations.DeleteModel( + name='GenericTeamToken', + ), + ] diff --git a/kfet/models.py b/kfet/models.py index ec146ad9..b06114d7 100644 --- a/kfet/models.py +++ b/kfet/models.py @@ -14,6 +14,8 @@ from datetime import date import re import hashlib +from .auth.models import GenericTeamToken # noqa + from .config import kfet_config from .utils import to_ukf @@ -710,7 +712,3 @@ class Operation(models.Model): return templates[self.type].format(nb=self.article_nb, article=self.article, amount=self.amount) - - -class GenericTeamToken(models.Model): - token = models.CharField(max_length = 50, unique = True) diff --git a/kfet/views.py b/kfet/views.py index c7eb677b..386eddb6 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -12,34 +12,30 @@ from django.views.generic.edit import CreateView, UpdateView from django.core.urlresolvers import reverse, reverse_lazy from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin -from django.contrib.auth import authenticate, login -from django.contrib.auth.decorators import login_required, permission_required -from django.contrib.auth.models import User, Permission, Group +from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User, Permission from django.http import JsonResponse, Http404 from django.forms import formset_factory from django.db import transaction from django.db.models import F, Sum, Prefetch, Count from django.db.models.functions import Coalesce from django.utils import timezone -from django.utils.crypto import get_random_string from django.utils.decorators import method_decorator -from django_cas_ng.views import logout as cas_logout_view - from gestioncof.models import CofProfile from kfet.config import kfet_config from kfet.decorators import teamkfet_required from kfet.models import ( Account, Checkout, Article, AccountNegative, - CheckoutStatement, GenericTeamToken, Supplier, SupplierArticle, Inventory, + CheckoutStatement, Supplier, SupplierArticle, Inventory, InventoryArticle, Order, OrderArticle, Operation, OperationGroup, TransferGroup, Transfer, ArticleCategory) from kfet.forms import ( AccountTriForm, AccountBalanceForm, AccountNoTriForm, UserForm, CofForm, UserRestrictTeamForm, UserGroupForm, AccountForm, CofRestrictForm, AccountPwdForm, AccountNegativeForm, UserRestrictForm, AccountRestrictForm, - GroupForm, CheckoutForm, CheckoutRestrictForm, CheckoutStatementCreateForm, + CheckoutForm, CheckoutRestrictForm, CheckoutStatementCreateForm, CheckoutStatementUpdateForm, ArticleForm, ArticleRestrictForm, KPsulOperationGroupForm, KPsulAccountForm, KPsulCheckoutForm, KPsulOperationFormSet, AddcostForm, FilterHistoryForm, @@ -54,25 +50,9 @@ import heapq import statistics from kfet.statistic import ScaleMixin, last_stats_manifest, tot_ventes, WeekScale - -@teamkfet_required -def login_genericteam(request): - # Check si besoin de déconnecter l'utilisateur de CAS - cas_logout = None - if request.user.profile.login_clipper: - # Récupèration de la vue de déconnexion de CAS - # Ici, car request sera modifié après - next_page = request.META.get('HTTP_REFERER', None) - cas_logout = cas_logout_view(request, next_page=next_page) - - # Authentification du compte générique - token = GenericTeamToken.objects.create(token=get_random_string(50)) - user = authenticate(username="kfet_genericteam", token=token.token) - login(request, user) - - messages.success(request, "Connecté en utilisateur partagé") - - return cas_logout or render(request, "kfet/login_genericteam.html") +from .auth.views import ( # noqa + account_group, login_genericteam, AccountGroupCreate, AccountGroupUpdate, +) def put_cleaned_data_in_dict(dict, form): @@ -505,37 +485,6 @@ def account_update(request, trigramme): }) -@permission_required('kfet.manage_perms') -def account_group(request): - user_pre = Prefetch( - 'user_set', - queryset=User.objects.select_related('profile__account_kfet'), - ) - groups = ( - Group.objects - .filter(name__icontains='K-Fêt') - .prefetch_related('permissions', user_pre) - ) - return render(request, 'kfet/account_group.html', { - 'groups': groups, - }) - - -class AccountGroupCreate(SuccessMessageMixin, CreateView): - model = Group - template_name = 'kfet/account_group_form.html' - form_class = GroupForm - success_message = 'Nouveau groupe : %(name)s' - success_url = reverse_lazy('kfet.account.group') - -class AccountGroupUpdate(SuccessMessageMixin, UpdateView): - queryset = Group.objects.filter(name__icontains='K-Fêt') - template_name = 'kfet/account_group_form.html' - form_class = GroupForm - success_message = 'Groupe modifié : %(name)s' - success_url = reverse_lazy('kfet.account.group') - - class AccountNegativeList(ListView): queryset = ( AccountNegative.objects