8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-23 15:33:45 +00:00

Merge branch 'update_django' into 'dev'

feat: ⬆️ Support for django 2.2 (#308)

See merge request re2o/re2o!604
This commit is contained in:
klafyvel 2021-02-14 10:22:50 +01:00
commit 8c88b6a6c9
169 changed files with 4818 additions and 2415 deletions

11
api/apps.py Normal file
View file

@ -0,0 +1,11 @@
"""
Configuration of api app.
"""
from django.apps import AppConfig
class ApiConfig(AppConfig):
"""Configuration of api app."""
name = "api"

View file

@ -31,12 +31,10 @@ from rest_framework.authentication import TokenAuthentication
class ExpiringTokenAuthentication(TokenAuthentication): class ExpiringTokenAuthentication(TokenAuthentication):
"""Authenticate a user if the provided token is valid and not expired. """Authenticate a user if the provided token is valid and not expired."""
"""
def authenticate_credentials(self, key): def authenticate_credentials(self, key):
"""See base class. Add the verification the token is not expired. """See base class. Add the verification the token is not expired."""
"""
base = super(ExpiringTokenAuthentication, self) base = super(ExpiringTokenAuthentication, self)
user, token = base.authenticate_credentials(key) user, token = base.authenticate_credentials(key)

View file

@ -22,8 +22,9 @@
"""Defines the permission classes used in the API. """Defines the permission classes used in the API.
""" """
from rest_framework import permissions, exceptions
from django.http import Http404 from django.http import Http404
from rest_framework import exceptions, permissions
from . import acl from . import acl

View file

@ -26,7 +26,7 @@
from collections import OrderedDict from collections import OrderedDict
from django.conf.urls import url from django.conf.urls import url
from django.core.urlresolvers import NoReverseMatch from django.urls import NoReverseMatch
from rest_framework import views from rest_framework import views
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.reverse import reverse from rest_framework.reverse import reverse

View file

@ -24,7 +24,6 @@
from rest_framework import serializers from rest_framework import serializers
# The namespace used for the API. It must match the namespace used in the # The namespace used for the API. It must match the namespace used in the
# urlpatterns to include the API URLs. # urlpatterns to include the API URLs.
API_NAMESPACE = "api" API_NAMESPACE = "api"
@ -59,7 +58,3 @@ class NamespacedHMSerializer(serializers.HyperlinkedModelSerializer):
serializer_related_field = NamespacedHRField serializer_related_field = NamespacedHRField
serializer_url_field = NamespacedHIField serializer_url_field = NamespacedHIField

View file

@ -28,12 +28,15 @@ can also be register. That way a complete API root page presenting all URLs
can be generated automatically. can be generated automatically.
""" """
from django.conf.urls import url, include
from importlib import import_module from importlib import import_module
from django.conf import settings
from django.urls import include, path
from . import views from . import views
from .routers import AllViewsRouter from .routers import AllViewsRouter
from django.conf import settings
app_name = "api"
router = AllViewsRouter() router = AllViewsRouter()
@ -65,4 +68,4 @@ for _url, view, name in urls_functional_view:
# TOKEN AUTHENTICATION # TOKEN AUTHENTICATION
router.register_view(r"token-auth", views.ObtainExpiringAuthToken) router.register_view(r"token-auth", views.ObtainExpiringAuthToken)
urlpatterns = [url(r"^", include(router.urls))] urlpatterns = [path("", include(router.urls))]

View file

@ -29,9 +29,9 @@ the response (JSON or other), the CSRF exempting, ...
import datetime import datetime
from django.conf import settings from django.conf import settings
from django.db.models import Q
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from rest_framework import viewsets, generics, views from django.db.models import Q
from rest_framework import generics, views, viewsets
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.response import Response from rest_framework.response import Response
@ -41,7 +41,6 @@ from .pagination import PageSizedPagination
from .permissions import ACLPermission from .permissions import ACLPermission
class ObtainExpiringAuthToken(ObtainAuthToken): class ObtainExpiringAuthToken(ObtainAuthToken):
"""Exposes a view to obtain a authentication token. """Exposes a view to obtain a authentication token.

View file

@ -29,8 +29,8 @@ from __future__ import unicode_literals
from django.contrib import admin from django.contrib import admin
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
from .models import Facture, Article, Banque, Paiement, Cotisation, Vente from .models import (Article, Banque, CostEstimate, Cotisation, CustomInvoice,
from .models import CustomInvoice, CostEstimate Facture, Paiement, Vente)
class FactureAdmin(VersionAdmin): class FactureAdmin(VersionAdmin):

View file

@ -23,13 +23,13 @@ from rest_framework import serializers
import cotisations.models as cotisations import cotisations.models as cotisations
import preferences.models as preferences import preferences.models as preferences
from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer from api.serializers import (NamespacedHIField, NamespacedHMSerializer,
NamespacedHRField)
from users.api.serializers import UserSerializer from users.api.serializers import UserSerializer
class FactureSerializer(NamespacedHMSerializer): class FactureSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Facture` objects. """Serialize `cotisations.models.Facture` objects."""
"""
class Meta: class Meta:
model = cotisations.Facture model = cotisations.Facture
@ -54,8 +54,7 @@ class BaseInvoiceSerializer(NamespacedHMSerializer):
class VenteSerializer(NamespacedHMSerializer): class VenteSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Vente` objects. """Serialize `cotisations.models.Vente` objects."""
"""
class Meta: class Meta:
model = cotisations.Vente model = cotisations.Vente
@ -74,17 +73,24 @@ class VenteSerializer(NamespacedHMSerializer):
class ArticleSerializer(NamespacedHMSerializer): class ArticleSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Article` objects. """Serialize `cotisations.models.Article` objects."""
"""
class Meta: class Meta:
model = cotisations.Article model = cotisations.Article
fields = ("name", "prix", "duration_membership", "duration_days_membership", "duration_connection", "duration_days_connection", "type_user", "api_url") fields = (
"name",
"prix",
"duration_membership",
"duration_days_membership",
"duration_connection",
"duration_days_connection",
"type_user",
"api_url",
)
class BanqueSerializer(NamespacedHMSerializer): class BanqueSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Banque` objects. """Serialize `cotisations.models.Banque` objects."""
"""
class Meta: class Meta:
model = cotisations.Banque model = cotisations.Banque
@ -92,8 +98,7 @@ class BanqueSerializer(NamespacedHMSerializer):
class PaiementSerializer(NamespacedHMSerializer): class PaiementSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Paiement` objects. """Serialize `cotisations.models.Paiement` objects."""
"""
class Meta: class Meta:
model = cotisations.Paiement model = cotisations.Paiement
@ -101,17 +106,23 @@ class PaiementSerializer(NamespacedHMSerializer):
class CotisationSerializer(NamespacedHMSerializer): class CotisationSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Cotisation` objects. """Serialize `cotisations.models.Cotisation` objects."""
"""
class Meta: class Meta:
model = cotisations.Cotisation model = cotisations.Cotisation
fields = ("vente", "type_cotisation", "date_start_con", "date_end_con", "date_start_memb", "date_end_memb", "api_url") fields = (
"vente",
"type_cotisation",
"date_start_con",
"date_end_con",
"date_start_memb",
"date_end_memb",
"api_url",
)
class ReminderUsersSerializer(UserSerializer): class ReminderUsersSerializer(UserSerializer):
"""Serialize the data about a mailing member. """Serialize the data about a mailing member."""
"""
class Meta(UserSerializer.Meta): class Meta(UserSerializer.Meta):
fields = ("get_full_name", "get_mail") fields = ("get_full_name", "get_mail")

View file

@ -27,12 +27,11 @@ urls_viewset = [
(r"cotisations/article", views.ArticleViewSet, None), (r"cotisations/article", views.ArticleViewSet, None),
(r"cotisations/banque", views.BanqueViewSet, None), (r"cotisations/banque", views.BanqueViewSet, None),
(r"cotisations/paiement", views.PaiementViewSet, None), (r"cotisations/paiement", views.PaiementViewSet, None),
(r"cotisations/cotisation", views.CotisationViewSet, None) (r"cotisations/cotisation", views.CotisationViewSet, None),
] ]
urls_view = [ urls_view = [
(r"cotisations/reminder-get-users", views.ReminderView), (r"cotisations/reminder-get-users", views.ReminderView),
# Deprecated # Deprecated
(r"reminder/get-users", views.ReminderView), (r"reminder/get-users", views.ReminderView),
] ]

View file

@ -19,71 +19,65 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from rest_framework import viewsets, generics from rest_framework import generics, viewsets
from . import serializers
import cotisations.models as cotisations import cotisations.models as cotisations
import preferences.models as preferences import preferences.models as preferences
from . import serializers
class FactureViewSet(viewsets.ReadOnlyModelViewSet): class FactureViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Facture` objects. """Exposes list and details of `cotisations.models.Facture` objects."""
"""
queryset = cotisations.Facture.objects.all() queryset = cotisations.Facture.objects.all()
serializer_class = serializers.FactureSerializer serializer_class = serializers.FactureSerializer
class FactureViewSet(viewsets.ReadOnlyModelViewSet): class FactureViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Facture` objects. """Exposes list and details of `cotisations.models.Facture` objects."""
"""
queryset = cotisations.BaseInvoice.objects.all() queryset = cotisations.BaseInvoice.objects.all()
serializer_class = serializers.BaseInvoiceSerializer serializer_class = serializers.BaseInvoiceSerializer
class VenteViewSet(viewsets.ReadOnlyModelViewSet): class VenteViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Vente` objects. """Exposes list and details of `cotisations.models.Vente` objects."""
"""
queryset = cotisations.Vente.objects.all() queryset = cotisations.Vente.objects.all()
serializer_class = serializers.VenteSerializer serializer_class = serializers.VenteSerializer
class ArticleViewSet(viewsets.ReadOnlyModelViewSet): class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Article` objects. """Exposes list and details of `cotisations.models.Article` objects."""
"""
queryset = cotisations.Article.objects.all() queryset = cotisations.Article.objects.all()
serializer_class = serializers.ArticleSerializer serializer_class = serializers.ArticleSerializer
class BanqueViewSet(viewsets.ReadOnlyModelViewSet): class BanqueViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Banque` objects. """Exposes list and details of `cotisations.models.Banque` objects."""
"""
queryset = cotisations.Banque.objects.all() queryset = cotisations.Banque.objects.all()
serializer_class = serializers.BanqueSerializer serializer_class = serializers.BanqueSerializer
class PaiementViewSet(viewsets.ReadOnlyModelViewSet): class PaiementViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Paiement` objects. """Exposes list and details of `cotisations.models.Paiement` objects."""
"""
queryset = cotisations.Paiement.objects.all() queryset = cotisations.Paiement.objects.all()
serializer_class = serializers.PaiementSerializer serializer_class = serializers.PaiementSerializer
class CotisationViewSet(viewsets.ReadOnlyModelViewSet): class CotisationViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Cotisation` objects. """Exposes list and details of `cotisations.models.Cotisation` objects."""
"""
queryset = cotisations.Cotisation.objects.all() queryset = cotisations.Cotisation.objects.all()
serializer_class = serializers.CotisationSerializer serializer_class = serializers.CotisationSerializer
class ReminderView(generics.ListAPIView): class ReminderView(generics.ListAPIView):
"""Output for users to remind an end of their subscription. """Output for users to remind an end of their subscription."""
"""
queryset = preferences.Reminder.objects.all() queryset = preferences.Reminder.objects.all()
serializer_class = serializers.ReminderSerializer serializer_class = serializers.ReminderSerializer

11
cotisations/apps.py Normal file
View file

@ -0,0 +1,11 @@
"""
Configuration of cotisations app.
"""
from django.apps import AppConfig
class CotisationsConfig(AppConfig):
"""Configuration of cotisations app."""
name = "cotisations"

View file

@ -37,25 +37,18 @@ of each of the method.
from __future__ import unicode_literals from __future__ import unicode_literals
from django import forms from django import forms
from django.db.models import Q
from django.forms import ModelForm, Form
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _ from django.forms import Form, ModelForm
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext_lazy as _
from re2o.field_permissions import FieldPermissionFormMixin from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin from re2o.mixins import FormRevMixin
from re2o.widgets import AutocompleteModelWidget from re2o.widgets import AutocompleteModelWidget
from .models import (
Article, from .models import (Article, Banque, CostEstimate, CustomInvoice, Facture,
Paiement, Paiement, Vente)
Facture,
Banque,
CustomInvoice,
Vente,
CostEstimate,
)
from .payment_methods import balance from .payment_methods import balance

View file

@ -0,0 +1,49 @@
# Generated by Django 2.2.18 on 2021-02-08 17:27
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0003_auto_20210124_1105'),
]
operations = [
migrations.AlterModelOptions(
name='article',
options={'permissions': (('buy_every_article', 'Can buy every article'),), 'verbose_name': 'article', 'verbose_name_plural': 'articles'},
),
migrations.AlterModelOptions(
name='banque',
options={'verbose_name': 'bank', 'verbose_name_plural': 'banks'},
),
migrations.AlterModelOptions(
name='baseinvoice',
options={},
),
migrations.AlterModelOptions(
name='costestimate',
options={},
),
migrations.AlterModelOptions(
name='cotisation',
options={'permissions': (('change_all_cotisation', 'Can edit the previous subscriptions'),), 'verbose_name': 'subscription', 'verbose_name_plural': 'subscriptions'},
),
migrations.AlterModelOptions(
name='custominvoice',
options={},
),
migrations.AlterModelOptions(
name='facture',
options={'permissions': (('change_facture_control', 'Can edit the "controlled" state'), ('change_all_facture', 'Can edit all the previous invoices')), 'verbose_name': 'invoice', 'verbose_name_plural': 'invoices'},
),
migrations.AlterModelOptions(
name='paiement',
options={'permissions': (('use_every_payment', 'Can use every payment method'),), 'verbose_name': 'payment method', 'verbose_name_plural': 'payment methods'},
),
migrations.AlterModelOptions(
name='vente',
options={'permissions': (('change_all_vente', 'Can edit all the previous purchases'),), 'verbose_name': 'purchase', 'verbose_name_plural': 'purchases'},
),
]

View file

@ -40,12 +40,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="cotisation", model_name="cotisation",
name="facture", name="facture",
field=models.OneToOneField(to="cotisations.Facture"), field=models.OneToOneField(to="cotisations.Facture", on_delete=models.CASCADE),
), ),
migrations.AlterField( migrations.AlterField(
model_name="vente", model_name="vente",
name="facture", name="facture",
field=models.ForeignKey(to="cotisations.Facture"), field=models.ForeignKey(to="cotisations.Facture", on_delete=models.CASCADE),
), ),
migrations.AlterField( migrations.AlterField(
model_name="vente", model_name="vente",

View file

@ -41,7 +41,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="cotisation", model_name="cotisation",
name="vente", name="vente",
field=models.OneToOneField(to="cotisations.Vente", null=True), field=models.OneToOneField(to="cotisations.Vente", null=True, on_delete=models.CASCADE),
preserve_default=False, preserve_default=False,
), ),
] ]

View file

@ -32,29 +32,29 @@ each.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from dateutil.relativedelta import relativedelta
from dateutil.relativedelta import relativedelta
from django.contrib import messages
from django.core.validators import MinValueValidator
from django.db import models from django.db import models
from django.db.models import Q, Max from django.db.models import Max, Q
from django.db.models.signals import post_save, post_delete from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.forms import ValidationError from django.forms import ValidationError
from django.core.validators import MinValueValidator from django.shortcuts import redirect
from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.urls import reverse
from django.shortcuts import redirect
from django.contrib import messages
from preferences.models import CotisationsOption import users.models
import users.signals
from cotisations.utils import (find_payment_method, send_mail_invoice,
send_mail_voucher)
from cotisations.validators import check_no_balance
from machines.models import regen from machines.models import regen
from preferences.models import CotisationsOption
from re2o.field_permissions import FieldPermissionModelMixin from re2o.field_permissions import FieldPermissionModelMixin
from re2o.mixins import AclMixin, RevMixin from re2o.mixins import AclMixin, RevMixin
import users.signals
import users.models
from cotisations.utils import find_payment_method, send_mail_invoice, send_mail_voucher
from cotisations.validators import check_no_balance
class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
@ -62,9 +62,6 @@ class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
class Meta: class Meta:
abstract = False abstract = False
permissions = (
("view_baseinvoice", _("Can view an base invoice object")),
)
# TODO : change prix to price # TODO : change prix to price
def prix(self): def prix(self):
@ -157,7 +154,6 @@ class Facture(BaseInvoice):
permissions = ( permissions = (
# TODO : change facture to invoice # TODO : change facture to invoice
("change_facture_control", _('Can edit the "controlled" state')), ("change_facture_control", _('Can edit the "controlled" state')),
("view_facture", _("Can view an invoice object")),
("change_all_facture", _("Can edit all the previous invoices")), ("change_all_facture", _("Can edit all the previous invoices")),
) )
verbose_name = _("invoice") verbose_name = _("invoice")
@ -364,7 +360,13 @@ def facture_post_save(**kwargs):
if facture.valid: if facture.valid:
user = facture.user user = facture.user
user.set_active() user.set_active()
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=True, mac_refresh=False) users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=True,
mac_refresh=False,
)
@receiver(post_delete, sender=Facture) @receiver(post_delete, sender=Facture)
@ -373,12 +375,16 @@ def facture_post_delete(**kwargs):
Synchronise the LDAP user after an invoice has been deleted. Synchronise the LDAP user after an invoice has been deleted.
""" """
user = kwargs["instance"].user user = kwargs["instance"].user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=True, mac_refresh=False) users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=True,
mac_refresh=False,
)
class CustomInvoice(BaseInvoice): class CustomInvoice(BaseInvoice):
class Meta:
permissions = (("view_custominvoice", _("Can view a custom invoice object")),)
recipient = models.CharField(max_length=255, verbose_name=_("recipient")) recipient = models.CharField(max_length=255, verbose_name=_("recipient"))
payment = models.CharField(max_length=255, verbose_name=_("payment type")) payment = models.CharField(max_length=255, verbose_name=_("payment type"))
@ -388,8 +394,6 @@ class CustomInvoice(BaseInvoice):
class CostEstimate(CustomInvoice): class CostEstimate(CustomInvoice):
class Meta:
permissions = (("view_costestimate", _("Can view a cost estimate object")),)
validity = models.DurationField( validity = models.DurationField(
verbose_name=_("period of validity"), help_text="DD HH:MM:SS" verbose_name=_("period of validity"), help_text="DD HH:MM:SS"
@ -489,10 +493,7 @@ class Vente(RevMixin, AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = ( permissions = (("change_all_vente", _("Can edit all the previous purchases")),)
("view_vente", _("Can view a purchase object")),
("change_all_vente", _("Can edit all the previous purchases")),
)
verbose_name = _("purchase") verbose_name = _("purchase")
verbose_name_plural = _("purchases") verbose_name_plural = _("purchases")
@ -669,7 +670,13 @@ def vente_post_save(**kwargs):
purchase.cotisation.save() purchase.cotisation.save()
user = purchase.facture.facture.user user = purchase.facture.facture.user
user.set_active() user.set_active()
users.signals.synchronise.send(sender=users.models.User, instance=user, base=True, access_refresh=True, mac_refresh=False) users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=True,
access_refresh=True,
mac_refresh=False,
)
# TODO : change vente to purchase # TODO : change vente to purchase
@ -685,7 +692,13 @@ def vente_post_delete(**kwargs):
return return
if purchase.type_cotisation: if purchase.type_cotisation:
user = invoice.user user = invoice.user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=True, access_refresh=True, mac_refresh=False) users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=True,
access_refresh=True,
mac_refresh=False,
)
class Article(RevMixin, AclMixin, models.Model): class Article(RevMixin, AclMixin, models.Model):
@ -749,10 +762,7 @@ class Article(RevMixin, AclMixin, models.Model):
unique_together = ("name", "type_user") unique_together = ("name", "type_user")
class Meta: class Meta:
permissions = ( permissions = (("buy_every_article", _("Can buy every article")),)
("view_article", _("Can view an article object")),
("buy_every_article", _("Can buy every article")),
)
verbose_name = "article" verbose_name = "article"
verbose_name_plural = "articles" verbose_name_plural = "articles"
@ -824,7 +834,6 @@ class Banque(RevMixin, AclMixin, models.Model):
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
class Meta: class Meta:
permissions = (("view_banque", _("Can view a bank object")),)
verbose_name = _("bank") verbose_name = _("bank")
verbose_name_plural = _("banks") verbose_name_plural = _("banks")
@ -855,10 +864,7 @@ class Paiement(RevMixin, AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = ( permissions = (("use_every_payment", _("Can use every payment method")),)
("view_paiement", _("Can view a payment method object")),
("use_every_payment", _("Can use every payment method")),
)
verbose_name = _("payment method") verbose_name = _("payment method")
verbose_name_plural = _("payment methods") verbose_name_plural = _("payment methods")
@ -980,7 +986,6 @@ class Cotisation(RevMixin, AclMixin, models.Model):
class Meta: class Meta:
permissions = ( permissions = (
("view_cotisation", _("Can view a subscription object")),
("change_all_cotisation", _("Can edit the previous subscriptions")), ("change_all_cotisation", _("Can edit the previous subscriptions")),
) )
verbose_name = _("subscription") verbose_name = _("subscription")

View file

@ -127,6 +127,6 @@ method to your model, where `form` is an instance of
""" """
from . import comnpay, cheque, balance, note_kfet, free, urls from . import balance, cheque, comnpay, free, note_kfet, urls
PAYMENT_METHODS = [comnpay, cheque, balance, note_kfet, free] PAYMENT_METHODS = [comnpay, cheque, balance, note_kfet, free]

View file

@ -18,12 +18,11 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.contrib import messages
from django.db import models from django.db import models
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from cotisations.models import Paiement from cotisations.models import Paiement
from cotisations.payment_methods.mixins import PaymentMethodMixin from cotisations.payment_methods.mixins import PaymentMethodMixin

View file

@ -20,8 +20,8 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django import forms from django import forms
from re2o.mixins import FormRevMixin
from cotisations.models import Facture as Invoice from cotisations.models import Facture as Invoice
from re2o.mixins import FormRevMixin
class InvoiceForm(FormRevMixin, forms.ModelForm): class InvoiceForm(FormRevMixin, forms.ModelForm):

View file

@ -19,6 +19,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.conf.urls import url from django.conf.urls import url
from . import views from . import views
urlpatterns = [url(r"^validate/(?P<invoice_pk>[0-9]+)$", views.cheque, name="validate")] urlpatterns = [url(r"^validate/(?P<invoice_pk>[0-9]+)$", views.cheque, name="validate")]

View file

@ -23,17 +23,17 @@
Here are defined some views dedicated to cheque payement. Here are defined some views dedicated to cheque payement.
""" """
from django.urls import reverse
from django.shortcuts import redirect, render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from cotisations.models import Facture as Invoice from cotisations.models import Facture as Invoice
from cotisations.utils import find_payment_method from cotisations.utils import find_payment_method
from .models import ChequePayment
from .forms import InvoiceForm from .forms import InvoiceForm
from .models import ChequePayment
@login_required @login_required

View file

@ -3,15 +3,15 @@ The module in charge of handling the negociation with Comnpay
for online payment for online payment
""" """
import time
from random import randrange
import base64 import base64
import hashlib import hashlib
import time
from collections import OrderedDict from collections import OrderedDict
from random import randrange
class Transaction: class Transaction:
""" The class representing a transaction with all the functions """The class representing a transaction with all the functions
used during the negociation used during the negociation
""" """
@ -35,7 +35,7 @@ class Transaction:
self.idTransaction = "" self.idTransaction = ""
def buildSecretHTML(self, produit="Produit", montant="0.00", idTransaction=""): def buildSecretHTML(self, produit="Produit", montant="0.00", idTransaction=""):
""" Build an HTML hidden form with the different parameters for the """Build an HTML hidden form with the different parameters for the
transaction transaction
""" """
if idTransaction == "": if idTransaction == "":

View file

@ -25,8 +25,8 @@ from django.utils.translation import ugettext_lazy as _
from cotisations.models import Paiement from cotisations.models import Paiement
from cotisations.payment_methods.mixins import PaymentMethodMixin from cotisations.payment_methods.mixins import PaymentMethodMixin
from re2o.aes_field import AESEncryptedField from re2o.aes_field import AESEncryptedField
from .comnpay import Transaction from .comnpay import Transaction
@ -53,8 +53,7 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
minimum_payment = models.DecimalField( minimum_payment = models.DecimalField(
verbose_name=_("minimum payment"), verbose_name=_("minimum payment"),
help_text=_( help_text=_(
"The minimal amount of money you have to use when paying with" "The minimal amount of money you have to use when paying with" " ComNpay."
" ComNpay."
), ),
max_digits=5, max_digits=5,
decimal_places=2, decimal_places=2,
@ -107,8 +106,7 @@ class ComnpayPayment(PaymentMethodMixin, models.Model):
return render(request, "cotisations/payment.html", r) return render(request, "cotisations/payment.html", r)
def check_price(self, price, *args, **kwargs): def check_price(self, price, *args, **kwargs):
"""Checks that the price meets the requirement to be paid with ComNpay. """Checks that the price meets the requirement to be paid with ComNpay."""
"""
return ( return (
(price >= self.minimum_payment), (price >= self.minimum_payment),
_( _(

View file

@ -19,6 +19,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.conf.urls import url from django.conf.urls import url
from . import views from . import views
urlpatterns = [ urlpatterns = [

View file

@ -25,16 +25,17 @@ Here are the views needed by comnpay
from collections import OrderedDict from collections import OrderedDict
from django.urls import reverse
from django.shortcuts import redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages from django.contrib import messages
from django.views.decorators.csrf import csrf_exempt from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.datastructures import MultiValueDictKeyError from django.utils.datastructures import MultiValueDictKeyError
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.http import HttpResponse, HttpResponseBadRequest from django.views.decorators.csrf import csrf_exempt
from cotisations.models import Facture from cotisations.models import Facture
from .comnpay import Transaction from .comnpay import Transaction
from .models import ComnpayPayment from .models import ComnpayPayment
@ -55,7 +56,10 @@ def accept_payment(request, factureid):
) )
# In case a cotisation was bought, inform the user, the # In case a cotisation was bought, inform the user, the
# cotisation time has been extended too # cotisation time has been extended too
if any(purchase.test_membership_or_connection() for purchase in invoice.vente_set.all()): if any(
purchase.test_membership_or_connection()
for purchase in invoice.vente_set.all()
):
messages.success( messages.success(
request, request,
_( _(

View file

@ -21,9 +21,10 @@
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from . import PAYMENT_METHODS
from cotisations.utils import find_payment_method from cotisations.utils import find_payment_method
from . import PAYMENT_METHODS
def payment_method_factory(payment, *args, creation=True, **kwargs): def payment_method_factory(payment, *args, creation=True, **kwargs):
"""This function finds the right payment method form for a given payment. """This function finds the right payment method form for a given payment.

View file

@ -18,10 +18,9 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.contrib import messages
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from cotisations.models import Paiement from cotisations.models import Paiement
from cotisations.payment_methods.mixins import PaymentMethodMixin from cotisations.payment_methods.mixins import PaymentMethodMixin
@ -43,8 +42,7 @@ class FreePayment(PaymentMethodMixin, models.Model):
) )
def end_payment(self, invoice, request): def end_payment(self, invoice, request):
"""Ends the payment normally. """Ends the payment normally."""
"""
return invoice.paiement.end_payment(invoice, request, use_payment_method=False) return invoice.paiement.end_payment(invoice, request, use_payment_method=False)
def check_price(self, price, user, *args, **kwargs): def check_price(self, price, user, *args, **kwargs):

View file

@ -19,17 +19,15 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.contrib import messages
from django.db import models from django.db import models
from django.shortcuts import render from django.shortcuts import redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from cotisations.models import Paiement from cotisations.models import Paiement
from cotisations.payment_methods.mixins import PaymentMethodMixin from cotisations.payment_methods.mixins import PaymentMethodMixin
from django.shortcuts import render, redirect
class NotePayment(PaymentMethodMixin, models.Model): class NotePayment(PaymentMethodMixin, models.Model):
""" """

View file

@ -5,8 +5,8 @@
""" Module pour dialoguer avec la NoteKfet2015 """ """ Module pour dialoguer avec la NoteKfet2015 """
import socket
import json import json
import socket
import ssl import ssl
import traceback import traceback

View file

@ -19,6 +19,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.conf.urls import url from django.conf.urls import url
from . import views from . import views
urlpatterns = [ urlpatterns = [

View file

@ -26,22 +26,23 @@ Here are the views needed by comnpay
from collections import OrderedDict from collections import OrderedDict
from django.urls import reverse
from django.shortcuts import redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages from django.contrib import messages
from django.views.decorators.csrf import csrf_exempt from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.datastructures import MultiValueDictKeyError from django.utils.datastructures import MultiValueDictKeyError
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.http import HttpResponse, HttpResponseBadRequest from django.views.decorators.csrf import csrf_exempt
from cotisations.models import Facture from cotisations.models import Facture
from cotisations.utils import find_payment_method from cotisations.utils import find_payment_method
from .models import NotePayment
from re2o.views import form
from re2o.acl import can_create, can_edit from re2o.acl import can_create, can_edit
from .note import login, don from re2o.views import form
from .forms import NoteCredentialForm from .forms import NoteCredentialForm
from .models import NotePayment
from .note import don, login
@login_required @login_required

View file

@ -19,10 +19,11 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from django.conf.urls import include, url from django.conf.urls import include, url
from . import comnpay, cheque, note_kfet
from . import cheque, comnpay, note_kfet
urlpatterns = [ urlpatterns = [
url(r"^comnpay/", include(comnpay.urls, namespace="comnpay")), url(r"^comnpay/", include((comnpay.urls, "comnpay"), namespace="comnpay")),
url(r"^cheque/", include(cheque.urls, namespace="cheque")), url(r"^cheque/", include((cheque.urls, "cheque"), namespace="cheque")),
url(r"^note_kfet/", include(note_kfet.urls, namespace="note_kfet")), url(r"^note_kfet/", include((note_kfet.urls, "note_kfet"), namespace="note_kfet")),
] ]

View file

@ -1,11 +1,12 @@
from django.test import TestCase
import datetime import datetime
from django.utils import timezone
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.test import TestCase
from django.utils import timezone
from users.models import User from users.models import User
from .models import Vente, Facture, Cotisation, Paiement
from .models import Cotisation, Facture, Paiement, Vente
class VenteModelTests(TestCase): class VenteModelTests(TestCase):
@ -111,12 +112,21 @@ class VenteModelTests(TestCase):
duration_days_connection=1, duration_days_connection=1,
duration_membership=0, duration_membership=0,
duration_deys_membership=1, duration_deys_membership=1,
prix=0 prix=0,
)
v.create_cotis(
date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)),
date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)),
) )
v.create_cotis(date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)), date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)))
v.save() v.save()
self.assertEqual(v.cotisation.date_end_con, timezone.make_aware(datetime.datetime(1998, 10, 17))) self.assertEqual(
self.assertEqual(v.cotisation.date_end_memb, timezone.make_aware(datetime.datetime(1998, 10, 17))) v.cotisation.date_end_con,
timezone.make_aware(datetime.datetime(1998, 10, 17)),
)
self.assertEqual(
v.cotisation.date_end_memb,
timezone.make_aware(datetime.datetime(1998, 10, 17)),
)
def test_one_day_cotisation_membership_only(self): def test_one_day_cotisation_membership_only(self):
""" """
@ -207,12 +217,21 @@ class VenteModelTests(TestCase):
duration_days_connection=0, duration_days_connection=0,
duration_membership=0, duration_membership=0,
duration_days_membership=1, duration_days_membership=1,
prix=0 prix=0,
)
v.create_cotis(
date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)),
date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)),
) )
v.create_cotis(date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)), date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)))
v.save() v.save()
self.assertEqual(v.cotisation.date_end_con, timezone.make_aware(datetime.datetime(1998, 10, 17))) self.assertEqual(
self.assertEqual(v.cotisation.date_end_memb, timezone.make_aware(datetime.datetime(1998, 10, 16))) v.cotisation.date_end_con,
timezone.make_aware(datetime.datetime(1998, 10, 17)),
)
self.assertEqual(
v.cotisation.date_end_memb,
timezone.make_aware(datetime.datetime(1998, 10, 16)),
)
def test_cotisation_membership_diff_connection(self): def test_cotisation_membership_diff_connection(self):
""" """
@ -252,9 +271,11 @@ class FactureModelTests(TestCase):
def setUp(self): def setUp(self):
self.user = User.objects.create(pseudo="testUserPlop", email="test@example.org") self.user = User.objects.create(pseudo="testUserPlop", email="test@example.org")
self.paiement = Paiement.objects.create(moyen="test payment") self.paiement = Paiement.objects.create(moyen="test payment")
def tearDown(self): def tearDown(self):
self.user.delete() self.user.delete()
self.paiement.delete() self.paiement.delete()
def test_cotisations_prolongation(self): def test_cotisations_prolongation(self):
"""When user already have one valid cotisation, the new one should be """When user already have one valid cotisation, the new one should be
added at the end of the existing one.""" added at the end of the existing one."""
@ -300,4 +321,3 @@ class FactureModelTests(TestCase):
raise e raise e
invoice1.delete() invoice1.delete()
invoice2.delete() invoice2.delete()

View file

@ -1,13 +1,14 @@
import datetime
from dateutil.relativedelta import relativedelta
from django.contrib.auth.models import Permission
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from django.contrib.auth.models import Permission
import datetime
from dateutil.relativedelta import relativedelta
from django.utils import timezone from django.utils import timezone
from users.models import Adherent from users.models import Adherent
from .models import Vente, Facture, Cotisation, Paiement, Article
from .models import Article, Cotisation, Facture, Paiement, Vente
class NewFactureTests(TestCase): class NewFactureTests(TestCase):

View file

@ -26,20 +26,19 @@ Used to generated PDF invoice.
""" """
import tempfile
from subprocess import Popen, PIPE
import os import os
import tempfile
from datetime import datetime from datetime import datetime
from subprocess import PIPE, Popen
from django.db import models
from django.template.loader import get_template
from django.http import HttpResponse
from django.conf import settings from django.conf import settings
from django.db import models
from django.http import HttpResponse
from django.template.loader import get_template
from django.utils.text import slugify from django.utils.text import slugify
from re2o.mixins import AclMixin, RevMixin
from preferences.models import CotisationsOption from preferences.models import CotisationsOption
from re2o.mixins import AclMixin, RevMixin
TEMP_PREFIX = getattr(settings, "TEX_TEMP_PREFIX", "render_tex-") TEMP_PREFIX = getattr(settings, "TEX_TEMP_PREFIX", "render_tex-")
CACHE_PREFIX = getattr(settings, "TEX_CACHE_PREFIX", "render-tex") CACHE_PREFIX = getattr(settings, "TEX_CACHE_PREFIX", "render-tex")

View file

@ -25,85 +25,84 @@ The defined URLs for the Cotisations app
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url from django.urls import path
from . import views, views_autocomplete from . import payment_methods, views, views_autocomplete
from . import payment_methods
app_name = "cotisations"
urlpatterns = [ urlpatterns = [
url(r"^new_facture/(?P<userid>[0-9]+)$", views.new_facture, name="new-facture"), path("new_facture/<int:userid>", views.new_facture, name="new-facture"),
url( path("edit_facture/<int:factureid>", views.edit_facture, name="edit-facture"),
r"^edit_facture/(?P<factureid>[0-9]+)$", views.edit_facture, name="edit-facture" path("del_facture/<int:factureid>", views.del_facture, name="del-facture"),
), path("facture_pdf/<int:factureid>", views.facture_pdf, name="facture-pdf"),
url(r"^del_facture/(?P<factureid>[0-9]+)$", views.del_facture, name="del-facture"), path("voucher_pdf/<int:factureid>", views.voucher_pdf, name="voucher-pdf"),
url(r"^facture_pdf/(?P<factureid>[0-9]+)$", views.facture_pdf, name="facture-pdf"), path("new_cost_estimate", views.new_cost_estimate, name="new-cost-estimate"),
url(r"^voucher_pdf/(?P<factureid>[0-9]+)$", views.voucher_pdf, name="voucher-pdf"), path("index_cost_estimate", views.index_cost_estimate, name="index-cost-estimate"),
url(r"^new_cost_estimate/$", views.new_cost_estimate, name="new-cost-estimate"), path(
url( "cost_estimate_pdf/<int:costestimateid>",
r"^index_cost_estimate/$", views.index_cost_estimate, name="index-cost-estimate"
),
url(
r"^cost_estimate_pdf/(?P<costestimateid>[0-9]+)$",
views.cost_estimate_pdf, views.cost_estimate_pdf,
name="cost-estimate-pdf", name="cost-estimate-pdf",
), ),
url( path(
r"^index_custom_invoice/$", "index_custom_invoice",
views.index_custom_invoice, views.index_custom_invoice,
name="index-custom-invoice", name="index-custom-invoice",
), ),
url( path(
r"^edit_cost_estimate/(?P<costestimateid>[0-9]+)$", "edit_cost_estimate/<int:costestimateid>",
views.edit_cost_estimate, views.edit_cost_estimate,
name="edit-cost-estimate", name="edit-cost-estimate",
), ),
url( path(
r"^cost_estimate_to_invoice/(?P<costestimateid>[0-9]+)$", "cost_estimate_to_invoice/<int:costestimateid>",
views.cost_estimate_to_invoice, views.cost_estimate_to_invoice,
name="cost-estimate-to-invoice", name="cost-estimate-to-invoice",
), ),
url( path(
r"^del_cost_estimate/(?P<costestimateid>[0-9]+)$", "del_cost_estimate/<int:costestimateid>",
views.del_cost_estimate, views.del_cost_estimate,
name="del-cost-estimate", name="del-cost-estimate",
), ),
url(r"^new_custom_invoice/$", views.new_custom_invoice, name="new-custom-invoice"), path("new_custom_invoice", views.new_custom_invoice, name="new-custom-invoice"),
url( path(
r"^edit_custom_invoice/(?P<custominvoiceid>[0-9]+)$", "edit_custom_invoice/<int:custominvoiceid>",
views.edit_custom_invoice, views.edit_custom_invoice,
name="edit-custom-invoice", name="edit-custom-invoice",
), ),
url( path(
r"^custom_invoice_pdf/(?P<custominvoiceid>[0-9]+)$", "custom_invoice_pdf/<int:custominvoiceid>",
views.custom_invoice_pdf, views.custom_invoice_pdf,
name="custom-invoice-pdf", name="custom-invoice-pdf",
), ),
url( path(
r"^del_custom_invoice/(?P<custominvoiceid>[0-9]+)$", "del_custom_invoice/<int:custominvoiceid>",
views.del_custom_invoice, views.del_custom_invoice,
name="del-custom-invoice", name="del-custom-invoice",
), ),
url(r"^credit_solde/(?P<userid>[0-9]+)$", views.credit_solde, name="credit-solde"), path("credit_solde/<int:userid>", views.credit_solde, name="credit-solde"),
url(r"^add_article/$", views.add_article, name="add-article"), path("add_article", views.add_article, name="add-article"),
url( path("edit_article/<int:articleid>", views.edit_article, name="edit-article"),
r"^edit_article/(?P<articleid>[0-9]+)$", views.edit_article, name="edit-article" path("del_article", views.del_article, name="del-article"),
), path("add_paiement", views.add_paiement, name="add-paiement"),
url(r"^del_article/$", views.del_article, name="del-article"), path(
url(r"^add_paiement/$", views.add_paiement, name="add-paiement"), "edit_paiement/<int:paiementid>",
url(
r"^edit_paiement/(?P<paiementid>[0-9]+)$",
views.edit_paiement, views.edit_paiement,
name="edit-paiement", name="edit-paiement",
), ),
url(r"^del_paiement/$", views.del_paiement, name="del-paiement"), path("del_paiement", views.del_paiement, name="del-paiement"),
url(r"^add_banque/$", views.add_banque, name="add-banque"), path("add_banque", views.add_banque, name="add-banque"),
url(r"^edit_banque/(?P<banqueid>[0-9]+)$", views.edit_banque, name="edit-banque"), path("edit_banque/<int:banqueid>", views.edit_banque, name="edit-banque"),
url(r"^del_banque/$", views.del_banque, name="del-banque"), path("del_banque", views.del_banque, name="del-banque"),
url(r"^index_article/$", views.index_article, name="index-article"), path("index_article", views.index_article, name="index-article"),
url(r"^index_banque/$", views.index_banque, name="index-banque"), path("index_banque", views.index_banque, name="index-banque"),
url(r"^index_paiement/$", views.index_paiement, name="index-paiement"), path("index_paiement", views.index_paiement, name="index-paiement"),
url(r"^control/$", views.control, name="control"), path("control", views.control, name="control"),
url(r"^$", views.index, name="index"), path("", views.index, name="index"),
### Autocomplete Views ### Autocomplete Views
url(r'^banque-autocomplete/$', views_autocomplete.BanqueAutocomplete.as_view(), name='banque-autocomplete',), path(
"banque-autocomplete",
views_autocomplete.BanqueAutocomplete.as_view(),
name="banque-autocomplete",
),
] + payment_methods.urls.urlpatterns ] + payment_methods.urls.urlpatterns

View file

@ -21,14 +21,16 @@
import os import os
from django.template.loader import get_template
from django.core.mail import EmailMessage from django.core.mail import EmailMessage
from django.template.loader import get_template
from preferences.models import (AssoOption, CotisationsOption, GeneralOption,
Mandate)
from re2o import settings
from re2o.mail_utils import send_mail_object from re2o.mail_utils import send_mail_object
from re2o.settings import LOGO_PATH
from .tex import create_pdf from .tex import create_pdf
from preferences.models import AssoOption, GeneralOption, CotisationsOption, Mandate
from re2o.settings import LOGO_PATH
from re2o import settings
def find_payment_method(payment): def find_payment_method(payment):
@ -74,7 +76,9 @@ def send_mail_invoice(invoice, request=None):
"tpl_path": os.path.join(settings.BASE_DIR, LOGO_PATH), "tpl_path": os.path.join(settings.BASE_DIR, LOGO_PATH),
} }
template = CotisationsOption.get_cached_value("invoice_template").template.name.split("/")[-1] template = CotisationsOption.get_cached_value(
"invoice_template"
).template.name.split("/")[-1]
pdf = create_pdf(template, ctx) pdf = create_pdf(template, ctx)
template = get_template("cotisations/email_invoice") template = get_template("cotisations/email_invoice")
@ -106,7 +110,9 @@ def send_mail_voucher(invoice, request=None):
"email": invoice.user.email, "email": invoice.user.email,
"phone": invoice.user.telephone, "phone": invoice.user.telephone,
"date_end": invoice.get_subscription().latest("date_end_memb").date_end_memb, "date_end": invoice.get_subscription().latest("date_end_memb").date_end_memb,
"date_begin": invoice.get_subscription().earliest("date_start_memb").date_start_memb, "date_begin": invoice.get_subscription()
.earliest("date_start_memb")
.date_start_memb,
} }
templatename = CotisationsOption.get_cached_value( templatename = CotisationsOption.get_cached_value(
"voucher_template" "voucher_template"

View file

@ -29,62 +29,38 @@ The different views used in the Cotisations module
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
import os import os
from django.urls import reverse
from django.shortcuts import render, redirect, get_object_or_404
from django.template.loader import render_to_string
from django.contrib.auth.decorators import login_required
from django.contrib import messages from django.contrib import messages
from django.db.models import ProtectedError from django.contrib.auth.decorators import login_required
from django.db.models import Q from django.db.models import ProtectedError, Q
from django.forms import modelformset_factory, formset_factory from django.forms import formset_factory, modelformset_factory
from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
# Import des models, forms et fonctions re2o # Import des models, forms et fonctions re2o
from reversion import revisions as reversion from reversion import revisions as reversion
from users.models import User
from re2o.settings import LOGO_PATH
from re2o import settings
from re2o.views import form
from re2o.base import SortTable, re2o_paginator
from re2o.acl import (
can_create,
can_edit,
can_delete,
can_view,
can_view_all,
can_delete_set,
can_change,
)
from preferences.models import AssoOption, GeneralOption, Mandate from preferences.models import AssoOption, GeneralOption, Mandate
from .models import ( from re2o import settings
Facture, from re2o.acl import (can_change, can_create, can_delete, can_delete_set,
Article, can_edit, can_view, can_view_all)
Vente, from re2o.base import SortTable, re2o_paginator
Paiement, from re2o.settings import LOGO_PATH
Banque, from re2o.views import form
CustomInvoice, from users.models import User
BaseInvoice,
CostEstimate, from .forms import (ArticleForm, BanqueForm, CostEstimateForm,
) CustomInvoiceForm, DelArticleForm, DelBanqueForm,
from .forms import ( DelPaiementForm, DiscountForm, FactureForm, PaiementForm,
FactureForm, RechargeForm, SelectArticleForm)
ArticleForm, from .models import (Article, Banque, BaseInvoice, CostEstimate, CustomInvoice,
DelArticleForm, Facture, Paiement, Vente)
PaiementForm,
DelPaiementForm,
BanqueForm,
DelBanqueForm,
SelectArticleForm,
RechargeForm,
CustomInvoiceForm,
DiscountForm,
CostEstimateForm,
)
from .tex import render_invoice, render_voucher, escape_chars
from .payment_methods.forms import payment_method_factory from .payment_methods.forms import payment_method_factory
from .tex import escape_chars, render_invoice, render_voucher
from .utils import find_payment_method from .utils import find_payment_method
@ -1057,12 +1033,15 @@ def voucher_pdf(request, invoice, **_kwargs):
"lastname": invoice.user.surname, "lastname": invoice.user.surname,
"email": invoice.user.email, "email": invoice.user.email,
"phone": invoice.user.telephone, "phone": invoice.user.telephone,
"date_end": invoice.get_subscription().latest("date_end_memb").date_end_memb, "date_end": invoice.get_subscription()
.latest("date_end_memb")
.date_end_memb,
"date_begin": invoice.date, "date_begin": invoice.date,
}, },
) )
def aff_profil(request,user):
def aff_profil(request, user):
"""View used to display the cotisations on a user's profil.""" """View used to display the cotisations on a user's profil."""
factures = Facture.objects.filter(user=user) factures = Facture.objects.filter(user=user)
@ -1074,13 +1053,13 @@ def aff_profil(request,user):
) )
pagination_large_number = GeneralOption.get_cached_value("pagination_large_number") pagination_large_number = GeneralOption.get_cached_value("pagination_large_number")
factures = re2o_paginator(request, factures,pagination_large_number) factures = re2o_paginator(request, factures, pagination_large_number)
context = { context = {
"users":user, "users": user,
"facture_list": factures, "facture_list": factures,
} }
return render_to_string( return render_to_string(
"cotisations/aff_profil.html",context=context,request=request,using=None "cotisations/aff_profil.html", context=context, request=request, using=None
) )

View file

@ -31,20 +31,13 @@ Here are defined the autocomplete class based view.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db.models import Q, Value, CharField from django.db.models import CharField, Q, Value
from .models import (
Banque
)
from re2o.acl import can_view_all
from re2o.views import AutocompleteViewMixin from re2o.views import AutocompleteViewMixin
from re2o.acl import ( from .models import Banque
can_view_all,
)
class BanqueAutocomplete(AutocompleteViewMixin): class BanqueAutocomplete(AutocompleteViewMixin):
obj_type = Banque obj_type = Banque

438
dev-requirements.txt Normal file
View file

@ -0,0 +1,438 @@
alabaster==0.7.12; python_version >= "3.5" \
--hash=sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359 \
--hash=sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02
appdirs==1.4.4; python_version >= "3.6" \
--hash=sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128 \
--hash=sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41
babel==2.9.0; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" \
--hash=sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5 \
--hash=sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05
black==20.8b1; python_version >= "3.6" \
--hash=sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea
certifi==2020.12.5; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" \
--hash=sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830 \
--hash=sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c
chardet==4.0.0; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" \
--hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5 \
--hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa
click==7.1.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" \
--hash=sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc \
--hash=sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a
colorama==0.4.4; python_version >= "3.5" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.5" and python_full_version >= "3.5.0" \
--hash=sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2 \
--hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b
coverage==5.4; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0" and python_version < "4") \
--hash=sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135 \
--hash=sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c \
--hash=sha256:9754a5c265f991317de2bac0c70a746efc2b695cf4d49f5d2cddeac36544fb44 \
--hash=sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3 \
--hash=sha256:b7f7421841f8db443855d2854e25914a79a1ff48ae92f70d0a5c2f8907ab98c9 \
--hash=sha256:4a780807e80479f281d47ee4af2eb2df3e4ccf4723484f77da0bb49d027e40a1 \
--hash=sha256:87c4b38288f71acd2106f5d94f575bc2136ea2887fdb5dfe18003c881fa6b370 \
--hash=sha256:c6809ebcbf6c1049002b9ac09c127ae43929042ec1f1dbd8bb1615f7cd9f70a0 \
--hash=sha256:ba7ca81b6d60a9f7a0b4b4e175dcc38e8fef4992673d9d6e6879fd6de00dd9b8 \
--hash=sha256:89fc12c6371bf963809abc46cced4a01ca4f99cba17be5e7d416ed7ef1245d19 \
--hash=sha256:4a8eb7785bd23565b542b01fb39115a975fefb4a82f23d407503eee2c0106247 \
--hash=sha256:7e40d3f8eb472c1509b12ac2a7e24158ec352fc8567b77ab02c0db053927e339 \
--hash=sha256:1ccae21a076d3d5f471700f6d30eb486da1626c380b23c70ae32ab823e453337 \
--hash=sha256:755c56beeacac6a24c8e1074f89f34f4373abce8b662470d3aa719ae304931f3 \
--hash=sha256:322549b880b2d746a7672bf6ff9ed3f895e9c9f108b714e7360292aa5c5d7cf4 \
--hash=sha256:60a3307a84ec60578accd35d7f0c71a3a971430ed7eca6567399d2b50ef37b8c \
--hash=sha256:1375bb8b88cb050a2d4e0da901001347a44302aeadb8ceb4b6e5aa373b8ea68f \
--hash=sha256:16baa799ec09cc0dcb43a10680573269d407c159325972dd7114ee7649e56c66 \
--hash=sha256:2f2cf7a42d4b7654c9a67b9d091ec24374f7c58794858bff632a2039cb15984d \
--hash=sha256:b62046592b44263fa7570f1117d372ae3f310222af1fc1407416f037fb3af21b \
--hash=sha256:812eaf4939ef2284d29653bcfee9665f11f013724f07258928f849a2306ea9f9 \
--hash=sha256:859f0add98707b182b4867359e12bde806b82483fb12a9ae868a77880fc3b7af \
--hash=sha256:04b14e45d6a8e159c9767ae57ecb34563ad93440fc1b26516a89ceb5b33c1ad5 \
--hash=sha256:ebfa374067af240d079ef97b8064478f3bf71038b78b017eb6ec93ede1b6bcec \
--hash=sha256:84df004223fd0550d0ea7a37882e5c889f3c6d45535c639ce9802293b39cd5c9 \
--hash=sha256:1b811662ecf72eb2d08872731636aee6559cae21862c36f74703be727b45df90 \
--hash=sha256:6b588b5cf51dc0fd1c9e19f622457cc74b7d26fe295432e434525f1c0fae02bc \
--hash=sha256:3fe50f1cac369b02d34ad904dfe0771acc483f82a1b54c5e93632916ba847b37 \
--hash=sha256:32ab83016c24c5cf3db2943286b85b0a172dae08c58d0f53875235219b676409 \
--hash=sha256:68fb816a5dd901c6aff352ce49e2a0ffadacdf9b6fae282a69e7a16a02dad5fb \
--hash=sha256:a636160680c6e526b84f85d304e2f0bb4e94f8284dd765a1911de9a40450b10a \
--hash=sha256:bb32ca14b4d04e172c541c69eec5f385f9a075b38fb22d765d8b0ce3af3a0c22 \
--hash=sha256:6c4d7165a4e8f41eca6b990c12ee7f44fef3932fac48ca32cecb3a1b2223c21f \
--hash=sha256:a565f48c4aae72d1d3d3f8e8fb7218f5609c964e9c6f68604608e5958b9c60c3 \
--hash=sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786 \
--hash=sha256:a839e25f07e428a87d17d857d9935dd743130e77ff46524abb992b962eb2076c \
--hash=sha256:6625e52b6f346a283c3d563d1fd8bae8956daafc64bb5bbd2b8f8a07608e3994 \
--hash=sha256:5bee3970617b3d74759b2d2df2f6a327d372f9732f9ccbf03fa591b5f7581e39 \
--hash=sha256:03ed2a641e412e42cc35c244508cf186015c217f0e4d496bf6d7078ebe837ae7 \
--hash=sha256:14a9f1887591684fb59fdba8feef7123a0da2424b0652e1b58dd5b9a7bb1188c \
--hash=sha256:9564ac7eb1652c3701ac691ca72934dd3009997c81266807aef924012df2f4b3 \
--hash=sha256:0f48fc7dc82ee14aeaedb986e175a429d24129b7eada1b7e94a864e4f0644dde \
--hash=sha256:107d327071061fd4f4a2587d14c389a27e4e5c93c7cba5f1f59987181903902f \
--hash=sha256:0cdde51bfcf6b6bd862ee9be324521ec619b20590787d1655d005c3fb175005f \
--hash=sha256:c67734cff78383a1f23ceba3b3239c7deefc62ac2b05fa6a47bcd565771e5880 \
--hash=sha256:c669b440ce46ae3abe9b2d44a913b5fd86bb19eb14a8701e88e3918902ecd345 \
--hash=sha256:c0ff1c1b4d13e2240821ef23c1efb1f009207cb3f56e16986f713c2b0e7cd37f \
--hash=sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b \
--hash=sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca
dataclasses==0.8; python_version >= "3.6" and python_version < "3.7" \
--hash=sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf \
--hash=sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97
django-autocomplete-light==3.8.1 \
--hash=sha256:4e84a6d95d272b0d7221614332e2bd54ffff15ec06e78947279398f6507ce225
django-bootstrap3==14.2.0; python_version >= "3.6" and python_version < "4.0" \
--hash=sha256:1fc3db37f29bcd159c0b00e1f15fd2d6dedf7551ce8bcca09072e33663c110de \
--hash=sha256:c7e6912b5127bea913bc8504399613758760954140162e048184624765e61904
django-ldapdb==1.5.1; python_version >= "3.6" \
--hash=sha256:5ea333c3130abbb86f8629766370a7f490878706469fce4e5008e41fb566392a \
--hash=sha256:2daee828a7eb1ce6ad0634ce5311339d77c08128829e575ff14d62a46771b86a
django-macaddress==1.7.0 \
--hash=sha256:342aa3421ee19acc3661d1705dad2ae674eaa7d06a02799f7a5dc394d6233275 \
--hash=sha256:db8beedcbd708aba3a6c9e83de6527efa2f9cc7dbe698406cebdfdbaf98c896d
django-rest-framework==0.1.0 \
--hash=sha256:47a8f496fa69e3b6bd79f68dd7a1527d907d6b77f009e9db7cf9bb21cc565e4a
django-reversion==3.0.9; python_version >= "3.6" \
--hash=sha256:a5af55f086a3f9c38be2f049c251e06005b9ed48ba7a109473736b1fc95a066f \
--hash=sha256:1b57127a136b969f4b843a915c72af271febe7f336469db6c27121f8adcad35c
django==2.2.18; python_version >= "3.5" \
--hash=sha256:0eaca08f236bf502a9773e53623f766cc3ceee6453cc41e6de1c8b80f07d2364 \
--hash=sha256:c9c994f5e0a032cbd45089798b52e4080f4dea7241c58e3e0636c54146480bb4
djangorestframework==3.12.2; python_version >= "3.5" \
--hash=sha256:0209bafcb7b5010fdfec784034f059d512256424de2a0f084cb82b096d6dd6a7 \
--hash=sha256:0898182b4737a7b584a2c73735d89816343369f259fea932d90dc78e35d8ac33
docstr-coverage==2.0.0 \
--hash=sha256:c5ff8d81128d2f56204fc9ecbb9c410cb18b654438d1b104868abb6ef400767e \
--hash=sha256:ee2c043ccbc6d1847cd93616acf4ebb3e7bc53ea84434111efddae9b69f3737a
docutils==0.16; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" \
--hash=sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af \
--hash=sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc
gitdb==4.0.5; python_version >= "3.4" \
--hash=sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac \
--hash=sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9
gitpython==3.1.13; python_version >= "3.4" \
--hash=sha256:c5347c81d232d9b8e7f47b68a83e5dc92e7952127133c5f2df9133f2c75a1b29 \
--hash=sha256:8621a7e777e276a5ec838b59280ba5272dd144a18169c36c903d8b38b99f750a
idna==2.10; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" \
--hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 \
--hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6
imagesize==1.2.0; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" \
--hash=sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1 \
--hash=sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1
importlib-metadata==1.7.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.6" and python_version < "3.8" and python_full_version >= "3.5.0" \
--hash=sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070 \
--hash=sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83
importlib-resources==5.1.0; python_version >= "3.6" and python_version < "3.7" \
--hash=sha256:885b8eae589179f661c909d699a546cf10d83692553e34dca1bf5eb06f7f6217 \
--hash=sha256:bfdad047bce441405a49cf8eb48ddce5e56c696e185f59147a8b79e75e9e6380
jinja2==2.11.3; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" \
--hash=sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419 \
--hash=sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6
markupsafe==1.1.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" \
--hash=sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161 \
--hash=sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7 \
--hash=sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183 \
--hash=sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b \
--hash=sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e \
--hash=sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f \
--hash=sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1 \
--hash=sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5 \
--hash=sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1 \
--hash=sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735 \
--hash=sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21 \
--hash=sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235 \
--hash=sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b \
--hash=sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f \
--hash=sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905 \
--hash=sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1 \
--hash=sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d \
--hash=sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff \
--hash=sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5 \
--hash=sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473 \
--hash=sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e \
--hash=sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f \
--hash=sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0 \
--hash=sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7 \
--hash=sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66 \
--hash=sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5 \
--hash=sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d \
--hash=sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193 \
--hash=sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e \
--hash=sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6 \
--hash=sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1 \
--hash=sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1 \
--hash=sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f \
--hash=sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2 \
--hash=sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c \
--hash=sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15 \
--hash=sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2 \
--hash=sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42 \
--hash=sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2 \
--hash=sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032 \
--hash=sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b \
--hash=sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b \
--hash=sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be \
--hash=sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c \
--hash=sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb \
--hash=sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014 \
--hash=sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850 \
--hash=sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85 \
--hash=sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621 \
--hash=sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39 \
--hash=sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8 \
--hash=sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b
mypy-extensions==0.4.3; python_version >= "3.6" \
--hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \
--hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8
mysqlclient==2.0.3; python_version >= "3.5" \
--hash=sha256:3381ca1a4f37ff1155fcfde20836b46416d66531add8843f6aa6d968982731c3 \
--hash=sha256:0ac0dd759c4ca02c35a9fedc24bc982cf75171651e8187c2495ec957a87dfff7 \
--hash=sha256:71c4b330cf2313bbda0307fc858cc9055e64493ba9bf28454d25cf8b3ee8d7f5 \
--hash=sha256:fc575093cf81b6605bed84653e48b277318b880dc9becf42dd47fa11ffd3e2b6 \
--hash=sha256:f6ebea7c008f155baeefe16c56cd3ee6239f7a5a9ae42396c2f1860f08a7c432
netaddr==0.8.0 \
--hash=sha256:9666d0232c32d2656e5e5f8d735f58fd6c7457ce52fc21c98d45f2af78f990ac \
--hash=sha256:d6cc57c7a07b1d9d2e917aa8b36ae8ce61c35ba3fcd1b83ca31c5a0ee2b5a243
packaging==20.9; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" \
--hash=sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a \
--hash=sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5
pathspec==0.8.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" \
--hash=sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d \
--hash=sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd
pillow==8.1.0; python_version >= "3.6" \
--hash=sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a \
--hash=sha256:93a473b53cc6e0b3ce6bf51b1b95b7b1e7e6084be3a07e40f79b42e83503fbf2 \
--hash=sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174 \
--hash=sha256:1d208e670abfeb41b6143537a681299ef86e92d2a3dac299d3cd6830d5c7bded \
--hash=sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d \
--hash=sha256:b09e10ec453de97f9a23a5aa5e30b334195e8d2ddd1ce76cc32e52ba63c8b31d \
--hash=sha256:b02a0b9f332086657852b1f7cb380f6a42403a6d9c42a4c34a561aa4530d5234 \
--hash=sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8 \
--hash=sha256:604815c55fd92e735f9738f65dabf4edc3e79f88541c221d292faec1904a4b17 \
--hash=sha256:cf6e33d92b1526190a1de904df21663c46a456758c0424e4f947ae9aa6088bf7 \
--hash=sha256:47c0d93ee9c8b181f353dbead6530b26980fe4f5485aa18be8f1fd3c3cbc685e \
--hash=sha256:96d4dc103d1a0fa6d47c6c55a47de5f5dafd5ef0114fa10c85a1fd8e0216284b \
--hash=sha256:7916cbc94f1c6b1301ac04510d0881b9e9feb20ae34094d3615a8a7c3db0dcc0 \
--hash=sha256:3de6b2ee4f78c6b3d89d184ade5d8fa68af0848f9b6b6da2b9ab7943ec46971a \
--hash=sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d \
--hash=sha256:f50e7a98b0453f39000619d845be8b06e611e56ee6e8186f7f60c3b1e2f0feae \
--hash=sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59 \
--hash=sha256:6c5275bd82711cd3dcd0af8ce0bb99113ae8911fc2952805f1d012de7d600a4c \
--hash=sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6 \
--hash=sha256:5e2fe3bb2363b862671eba632537cd3a823847db4d98be95690b7e382f3d6378 \
--hash=sha256:7612520e5e1a371d77e1d1ca3a3ee6227eef00d0a9cddb4ef7ecb0b7396eddf7 \
--hash=sha256:d673c4990acd016229a5c1c4ee8a9e6d8f481b27ade5fc3d95938697fa443ce0 \
--hash=sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b \
--hash=sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865 \
--hash=sha256:a3d3e086474ef12ef13d42e5f9b7bbf09d39cf6bd4940f982263d6954b13f6a9 \
--hash=sha256:731ca5aabe9085160cf68b2dbef95fc1991015bc0a3a6ea46a371ab88f3d0913 \
--hash=sha256:bba80df38cfc17f490ec651c73bb37cd896bc2400cfba27d078c2135223c1206 \
--hash=sha256:c3d911614b008e8a576b8e5303e3db29224b455d3d66d1b2848ba6ca83f9ece9 \
--hash=sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032 \
--hash=sha256:81c3fa9a75d9f1afafdb916d5995633f319db09bd773cb56b8e39f1e98d90820 \
--hash=sha256:b6f00ad5ebe846cc91763b1d0c6d30a8042e02b2316e27b05de04fa6ec831ec5 \
--hash=sha256:887668e792b7edbfb1d3c9d8b5d8c859269a0f0eba4dda562adb95500f60dbba
psycopg2==2.8.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") \
--hash=sha256:068115e13c70dc5982dfc00c5d70437fe37c014c808acce119b5448361c03725 \
--hash=sha256:d160744652e81c80627a909a0e808f3c6653a40af435744de037e3172cf277f5 \
--hash=sha256:b8cae8b2f022efa1f011cc753adb9cbadfa5a184431d09b273fb49b4167561ad \
--hash=sha256:f22ea9b67aea4f4a1718300908a2fb62b3e4276cf00bd829a97ab5894af42ea3 \
--hash=sha256:26e7fd115a6db75267b325de0fba089b911a4a12ebd3d0b5e7acb7028bc46821 \
--hash=sha256:00195b5f6832dbf2876b8bf77f12bdce648224c89c880719c745b90515233301 \
--hash=sha256:a49833abfdede8985ba3f3ec641f771cca215479f41523e99dace96d5b8cce2a \
--hash=sha256:f974c96fca34ae9e4f49839ba6b78addf0346777b46c4da27a7bf54f48d3057d \
--hash=sha256:6a3d9efb6f36f1fe6aa8dbb5af55e067db802502c55a9defa47c5a1dad41df84 \
--hash=sha256:56fee7f818d032f802b8eed81ef0c1232b8b42390df189cab9cfa87573fe52c5 \
--hash=sha256:ad2fe8a37be669082e61fb001c185ffb58867fdbb3e7a6b0b0d2ffe232353a3e \
--hash=sha256:56007a226b8e95aa980ada7abdea6b40b75ce62a433bd27cec7a8178d57f4051 \
--hash=sha256:2c93d4d16933fea5bbacbe1aaf8fa8c1348740b2e50b3735d1b0bf8154cbf0f3 \
--hash=sha256:d5062ae50b222da28253059880a871dc87e099c25cb68acf613d9d227413d6f7 \
--hash=sha256:fb23f6c71107c37fd667cb4ea363ddeb936b348bbd6449278eb92c189699f543
pyasn1-modules==0.2.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e \
--hash=sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199 \
--hash=sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405 \
--hash=sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb \
--hash=sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8 \
--hash=sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74 \
--hash=sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d \
--hash=sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45 \
--hash=sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4 \
--hash=sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811 \
--hash=sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed \
--hash=sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0 \
--hash=sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd
pyasn1==0.4.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3 \
--hash=sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf \
--hash=sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00 \
--hash=sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8 \
--hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \
--hash=sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86 \
--hash=sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7 \
--hash=sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576 \
--hash=sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12 \
--hash=sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2 \
--hash=sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359 \
--hash=sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776 \
--hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba
pycrypto==2.6.1 \
--hash=sha256:f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c
pygments==2.7.4; python_version >= "3.5" \
--hash=sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435 \
--hash=sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337
pyparsing==2.4.7; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5" \
--hash=sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b \
--hash=sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1
python-dateutil==2.8.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") \
--hash=sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c \
--hash=sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a
python-ldap==3.3.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:4711cacf013e298754abd70058ccc995758177fb425f1c2d30e71adfc1d00aa5
pytz==2021.1; python_version >= "3.6" and python_version < "4.0" and (python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.5") \
--hash=sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798 \
--hash=sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da
pyyaml==5.4.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" \
--hash=sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922 \
--hash=sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393 \
--hash=sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8 \
--hash=sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185 \
--hash=sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253 \
--hash=sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc \
--hash=sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5 \
--hash=sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df \
--hash=sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018 \
--hash=sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63 \
--hash=sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b \
--hash=sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf \
--hash=sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46 \
--hash=sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb \
--hash=sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc \
--hash=sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696 \
--hash=sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77 \
--hash=sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183 \
--hash=sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10 \
--hash=sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db \
--hash=sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e
regex==2020.11.13; python_version >= "3.6" \
--hash=sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85 \
--hash=sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70 \
--hash=sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee \
--hash=sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5 \
--hash=sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7 \
--hash=sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31 \
--hash=sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa \
--hash=sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6 \
--hash=sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e \
--hash=sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884 \
--hash=sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b \
--hash=sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88 \
--hash=sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0 \
--hash=sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1 \
--hash=sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0 \
--hash=sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512 \
--hash=sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba \
--hash=sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538 \
--hash=sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4 \
--hash=sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444 \
--hash=sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f \
--hash=sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d \
--hash=sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af \
--hash=sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f \
--hash=sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b \
--hash=sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8 \
--hash=sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5 \
--hash=sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b \
--hash=sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c \
--hash=sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683 \
--hash=sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc \
--hash=sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364 \
--hash=sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e \
--hash=sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e \
--hash=sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917 \
--hash=sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b \
--hash=sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9 \
--hash=sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c \
--hash=sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f \
--hash=sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d \
--hash=sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562
requests==2.25.1; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.5" \
--hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e \
--hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804
rope==0.18.0 \
--hash=sha256:786b5c38c530d4846aa68a42604f61b4e69a493390e3ca11b88df0fbfdc3ed04
six==1.15.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" \
--hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced \
--hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259
smmap==3.0.5; python_version >= "3.4" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.4" \
--hash=sha256:7bfcf367828031dc893530a29cb35eb8c8f2d7c8f2d0989354d75d24c8573714 \
--hash=sha256:84c2751ef3072d4f6b2785ec7ee40244c6f45eb934d9e543e2c51f1bd3d54c50
snowballstemmer==2.1.0; python_version >= "3.5" \
--hash=sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2 \
--hash=sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914
sphinx-rtd-theme==0.5.1 \
--hash=sha256:fa6bebd5ab9a73da8e102509a86f3fcc36dec04a0b52ea80e5a033b2aba00113 \
--hash=sha256:eda689eda0c7301a80cf122dad28b1861e5605cbf455558f3775e1e8200e83a5
sphinx==3.4.3; python_version >= "3.5" \
--hash=sha256:c314c857e7cd47c856d2c5adff514ac2e6495f8b8e0f886a8a37e9305dfea0d8 \
--hash=sha256:41cad293f954f7d37f803d97eb184158cfd90f51195131e94875bc07cd08b93c
sphinxcontrib-applehelp==1.0.2; python_version >= "3.5" \
--hash=sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58 \
--hash=sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a
sphinxcontrib-devhelp==1.0.2; python_version >= "3.5" \
--hash=sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4 \
--hash=sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e
sphinxcontrib-htmlhelp==1.0.3; python_version >= "3.5" \
--hash=sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b \
--hash=sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f
sphinxcontrib-jsmath==1.0.1; python_version >= "3.5" \
--hash=sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8 \
--hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178
sphinxcontrib-qthelp==1.0.3; python_version >= "3.5" \
--hash=sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72 \
--hash=sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6
sphinxcontrib-serializinghtml==1.1.4; python_version >= "3.5" \
--hash=sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc \
--hash=sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a
sqlparse==0.4.1; python_version >= "3.6" and python_version < "4.0" \
--hash=sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0 \
--hash=sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8
toml==0.10.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" \
--hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \
--hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f
typed-ast==1.4.2; python_version >= "3.6" \
--hash=sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70 \
--hash=sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487 \
--hash=sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412 \
--hash=sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400 \
--hash=sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606 \
--hash=sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64 \
--hash=sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07 \
--hash=sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc \
--hash=sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a \
--hash=sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151 \
--hash=sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3 \
--hash=sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41 \
--hash=sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f \
--hash=sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581 \
--hash=sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37 \
--hash=sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd \
--hash=sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496 \
--hash=sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc \
--hash=sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10 \
--hash=sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea \
--hash=sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787 \
--hash=sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2 \
--hash=sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937 \
--hash=sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1 \
--hash=sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6 \
--hash=sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166 \
--hash=sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d \
--hash=sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b \
--hash=sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440 \
--hash=sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a
typing-extensions==3.7.4.3; python_version >= "3.6" \
--hash=sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f \
--hash=sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918 \
--hash=sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c
urllib3==1.26.3; python_version >= "3.5" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.5" \
--hash=sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80 \
--hash=sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73
zipp==3.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_version < "3.7" and python_full_version >= "3.5.0" \
--hash=sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108 \
--hash=sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb

View file

@ -34,12 +34,12 @@ https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_pyth
Inspired by Daniel Stan in Crans Inspired by Daniel Stan in Crans
""" """
import logging
import os import os
import sys import sys
import logging
import traceback import traceback
import radiusd # Magic module freeradius (radiusd.py is dummy)
import radiusd # Magic module freeradius (radiusd.py is dummy)
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
from django.db.models import Q from django.db.models import Q
@ -54,11 +54,10 @@ os.chdir(proj_path)
# This is so models get loaded. # This is so models get loaded.
application = get_wsgi_application() application = get_wsgi_application()
from machines.models import Interface, IpList, Nas, Domain from machines.models import Domain, Interface, IpList, Nas
from preferences.models import RadiusOption
from topologie.models import Port, Switch from topologie.models import Port, Switch
from users.models import User from users.models import User
from preferences.models import RadiusOption
# Logging # Logging
@ -162,8 +161,7 @@ def authorize(data):
@radius_event @radius_event
def post_auth(data): def post_auth(data):
""" Function called after the user is authenticated """Function called after the user is authenticated"""
"""
nas = data.get("NAS-IP-Address", data.get("NAS-Identifier", None)) nas = data.get("NAS-IP-Address", data.get("NAS-Identifier", None))
nas_instance = find_nas_from_request(nas) nas_instance = find_nas_from_request(nas)

142
ldap-requirements.txt Normal file
View file

@ -0,0 +1,142 @@
django-autocomplete-light==3.8.1 \
--hash=sha256:4e84a6d95d272b0d7221614332e2bd54ffff15ec06e78947279398f6507ce225
django-bootstrap3==14.2.0; python_version >= "3.6" and python_version < "4.0" \
--hash=sha256:1fc3db37f29bcd159c0b00e1f15fd2d6dedf7551ce8bcca09072e33663c110de \
--hash=sha256:c7e6912b5127bea913bc8504399613758760954140162e048184624765e61904
django-ldapdb==1.5.1; python_version >= "3.6" \
--hash=sha256:5ea333c3130abbb86f8629766370a7f490878706469fce4e5008e41fb566392a \
--hash=sha256:2daee828a7eb1ce6ad0634ce5311339d77c08128829e575ff14d62a46771b86a
django-macaddress==1.7.0 \
--hash=sha256:342aa3421ee19acc3661d1705dad2ae674eaa7d06a02799f7a5dc394d6233275 \
--hash=sha256:db8beedcbd708aba3a6c9e83de6527efa2f9cc7dbe698406cebdfdbaf98c896d
django-rest-framework==0.1.0 \
--hash=sha256:47a8f496fa69e3b6bd79f68dd7a1527d907d6b77f009e9db7cf9bb21cc565e4a
django-reversion==3.0.9; python_version >= "3.6" \
--hash=sha256:a5af55f086a3f9c38be2f049c251e06005b9ed48ba7a109473736b1fc95a066f \
--hash=sha256:1b57127a136b969f4b843a915c72af271febe7f336469db6c27121f8adcad35c
django==2.2.18; python_version >= "3.5" \
--hash=sha256:0eaca08f236bf502a9773e53623f766cc3ceee6453cc41e6de1c8b80f07d2364 \
--hash=sha256:c9c994f5e0a032cbd45089798b52e4080f4dea7241c58e3e0636c54146480bb4
djangorestframework==3.12.2; python_version >= "3.5" \
--hash=sha256:0209bafcb7b5010fdfec784034f059d512256424de2a0f084cb82b096d6dd6a7 \
--hash=sha256:0898182b4737a7b584a2c73735d89816343369f259fea932d90dc78e35d8ac33
gitdb==4.0.5; python_version >= "3.4" \
--hash=sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac \
--hash=sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9
gitpython==3.1.13; python_version >= "3.4" \
--hash=sha256:c5347c81d232d9b8e7f47b68a83e5dc92e7952127133c5f2df9133f2c75a1b29 \
--hash=sha256:8621a7e777e276a5ec838b59280ba5272dd144a18169c36c903d8b38b99f750a
importlib-metadata==1.7.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.6" and python_version < "3.8" and python_full_version >= "3.5.0" \
--hash=sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070 \
--hash=sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83
importlib-resources==5.1.0; python_version >= "3.6" and python_version < "3.7" \
--hash=sha256:885b8eae589179f661c909d699a546cf10d83692553e34dca1bf5eb06f7f6217 \
--hash=sha256:bfdad047bce441405a49cf8eb48ddce5e56c696e185f59147a8b79e75e9e6380
mysqlclient==2.0.3; python_version >= "3.5" \
--hash=sha256:3381ca1a4f37ff1155fcfde20836b46416d66531add8843f6aa6d968982731c3 \
--hash=sha256:0ac0dd759c4ca02c35a9fedc24bc982cf75171651e8187c2495ec957a87dfff7 \
--hash=sha256:71c4b330cf2313bbda0307fc858cc9055e64493ba9bf28454d25cf8b3ee8d7f5 \
--hash=sha256:fc575093cf81b6605bed84653e48b277318b880dc9becf42dd47fa11ffd3e2b6 \
--hash=sha256:f6ebea7c008f155baeefe16c56cd3ee6239f7a5a9ae42396c2f1860f08a7c432
netaddr==0.8.0 \
--hash=sha256:9666d0232c32d2656e5e5f8d735f58fd6c7457ce52fc21c98d45f2af78f990ac \
--hash=sha256:d6cc57c7a07b1d9d2e917aa8b36ae8ce61c35ba3fcd1b83ca31c5a0ee2b5a243
pillow==8.1.0; python_version >= "3.6" \
--hash=sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a \
--hash=sha256:93a473b53cc6e0b3ce6bf51b1b95b7b1e7e6084be3a07e40f79b42e83503fbf2 \
--hash=sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174 \
--hash=sha256:1d208e670abfeb41b6143537a681299ef86e92d2a3dac299d3cd6830d5c7bded \
--hash=sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d \
--hash=sha256:b09e10ec453de97f9a23a5aa5e30b334195e8d2ddd1ce76cc32e52ba63c8b31d \
--hash=sha256:b02a0b9f332086657852b1f7cb380f6a42403a6d9c42a4c34a561aa4530d5234 \
--hash=sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8 \
--hash=sha256:604815c55fd92e735f9738f65dabf4edc3e79f88541c221d292faec1904a4b17 \
--hash=sha256:cf6e33d92b1526190a1de904df21663c46a456758c0424e4f947ae9aa6088bf7 \
--hash=sha256:47c0d93ee9c8b181f353dbead6530b26980fe4f5485aa18be8f1fd3c3cbc685e \
--hash=sha256:96d4dc103d1a0fa6d47c6c55a47de5f5dafd5ef0114fa10c85a1fd8e0216284b \
--hash=sha256:7916cbc94f1c6b1301ac04510d0881b9e9feb20ae34094d3615a8a7c3db0dcc0 \
--hash=sha256:3de6b2ee4f78c6b3d89d184ade5d8fa68af0848f9b6b6da2b9ab7943ec46971a \
--hash=sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d \
--hash=sha256:f50e7a98b0453f39000619d845be8b06e611e56ee6e8186f7f60c3b1e2f0feae \
--hash=sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59 \
--hash=sha256:6c5275bd82711cd3dcd0af8ce0bb99113ae8911fc2952805f1d012de7d600a4c \
--hash=sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6 \
--hash=sha256:5e2fe3bb2363b862671eba632537cd3a823847db4d98be95690b7e382f3d6378 \
--hash=sha256:7612520e5e1a371d77e1d1ca3a3ee6227eef00d0a9cddb4ef7ecb0b7396eddf7 \
--hash=sha256:d673c4990acd016229a5c1c4ee8a9e6d8f481b27ade5fc3d95938697fa443ce0 \
--hash=sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b \
--hash=sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865 \
--hash=sha256:a3d3e086474ef12ef13d42e5f9b7bbf09d39cf6bd4940f982263d6954b13f6a9 \
--hash=sha256:731ca5aabe9085160cf68b2dbef95fc1991015bc0a3a6ea46a371ab88f3d0913 \
--hash=sha256:bba80df38cfc17f490ec651c73bb37cd896bc2400cfba27d078c2135223c1206 \
--hash=sha256:c3d911614b008e8a576b8e5303e3db29224b455d3d66d1b2848ba6ca83f9ece9 \
--hash=sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032 \
--hash=sha256:81c3fa9a75d9f1afafdb916d5995633f319db09bd773cb56b8e39f1e98d90820 \
--hash=sha256:b6f00ad5ebe846cc91763b1d0c6d30a8042e02b2316e27b05de04fa6ec831ec5 \
--hash=sha256:887668e792b7edbfb1d3c9d8b5d8c859269a0f0eba4dda562adb95500f60dbba
psycopg2==2.8.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") \
--hash=sha256:068115e13c70dc5982dfc00c5d70437fe37c014c808acce119b5448361c03725 \
--hash=sha256:d160744652e81c80627a909a0e808f3c6653a40af435744de037e3172cf277f5 \
--hash=sha256:b8cae8b2f022efa1f011cc753adb9cbadfa5a184431d09b273fb49b4167561ad \
--hash=sha256:f22ea9b67aea4f4a1718300908a2fb62b3e4276cf00bd829a97ab5894af42ea3 \
--hash=sha256:26e7fd115a6db75267b325de0fba089b911a4a12ebd3d0b5e7acb7028bc46821 \
--hash=sha256:00195b5f6832dbf2876b8bf77f12bdce648224c89c880719c745b90515233301 \
--hash=sha256:a49833abfdede8985ba3f3ec641f771cca215479f41523e99dace96d5b8cce2a \
--hash=sha256:f974c96fca34ae9e4f49839ba6b78addf0346777b46c4da27a7bf54f48d3057d \
--hash=sha256:6a3d9efb6f36f1fe6aa8dbb5af55e067db802502c55a9defa47c5a1dad41df84 \
--hash=sha256:56fee7f818d032f802b8eed81ef0c1232b8b42390df189cab9cfa87573fe52c5 \
--hash=sha256:ad2fe8a37be669082e61fb001c185ffb58867fdbb3e7a6b0b0d2ffe232353a3e \
--hash=sha256:56007a226b8e95aa980ada7abdea6b40b75ce62a433bd27cec7a8178d57f4051 \
--hash=sha256:2c93d4d16933fea5bbacbe1aaf8fa8c1348740b2e50b3735d1b0bf8154cbf0f3 \
--hash=sha256:d5062ae50b222da28253059880a871dc87e099c25cb68acf613d9d227413d6f7 \
--hash=sha256:fb23f6c71107c37fd667cb4ea363ddeb936b348bbd6449278eb92c189699f543
pyasn1-modules==0.2.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e \
--hash=sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199 \
--hash=sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405 \
--hash=sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb \
--hash=sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8 \
--hash=sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74 \
--hash=sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d \
--hash=sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45 \
--hash=sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4 \
--hash=sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811 \
--hash=sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed \
--hash=sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0 \
--hash=sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd
pyasn1==0.4.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3 \
--hash=sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf \
--hash=sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00 \
--hash=sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8 \
--hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \
--hash=sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86 \
--hash=sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7 \
--hash=sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576 \
--hash=sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12 \
--hash=sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2 \
--hash=sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359 \
--hash=sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776 \
--hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba
pycrypto==2.6.1 \
--hash=sha256:f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c
python-dateutil==2.8.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") \
--hash=sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c \
--hash=sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a
python-ldap==3.3.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:4711cacf013e298754abd70058ccc995758177fb425f1c2d30e71adfc1d00aa5
pytz==2021.1; python_version >= "3.6" and python_version < "4.0" \
--hash=sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798 \
--hash=sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da
six==1.15.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" \
--hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced \
--hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259
smmap==3.0.5; python_version >= "3.4" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.4" \
--hash=sha256:7bfcf367828031dc893530a29cb35eb8c8f2d7c8f2d0989354d75d24c8573714 \
--hash=sha256:84c2751ef3072d4f6b2785ec7ee40244c6f45eb934d9e543e2c51f1bd3d54c50
sqlparse==0.4.1; python_version >= "3.6" and python_version < "4.0" \
--hash=sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0 \
--hash=sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8
zipp==3.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_version < "3.7" and python_full_version >= "3.5.0" \
--hash=sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108 \
--hash=sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb

View file

@ -1,11 +1,8 @@
from django.contrib import admin from django.contrib import admin
from .models import ( from .models import (LdapServiceUser, LdapServiceUserGroup, LdapUser,
LdapUser, LdapUserGroup)
LdapServiceUser,
LdapServiceUserGroup,
LdapUserGroup,
)
class LdapUserAdmin(admin.ModelAdmin): class LdapUserAdmin(admin.ModelAdmin):
"""LdapUser Admin view. Can't change password, manage """LdapUser Admin view. Can't change password, manage
@ -15,6 +12,7 @@ class LdapUserAdmin(admin.ModelAdmin):
Django ModelAdmin: Apply on django ModelAdmin Django ModelAdmin: Apply on django ModelAdmin
""" """
list_display = ("name", "uidNumber", "login_shell") list_display = ("name", "uidNumber", "login_shell")
exclude = ("user_password", "sambat_nt_password") exclude = ("user_password", "sambat_nt_password")
search_fields = ("name",) search_fields = ("name",)

View file

@ -2,4 +2,4 @@ from django.apps import AppConfig
class LdapSyncConfig(AppConfig): class LdapSyncConfig(AppConfig):
name = 'ldap_sync' name = "ldap_sync"

View file

@ -18,11 +18,12 @@
import subprocess import subprocess
from base64 import decodebytes from base64 import decodebytes
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from users.models import User, ListRight from ldap_sync.models import (synchronise_serviceuser, synchronise_user,
from ldap_sync.models import synchronise_user, synchronise_serviceuser, synchronise_usergroup synchronise_usergroup)
from users.models import ListRight, User
def split_lines(lines): def split_lines(lines):

View file

@ -19,8 +19,8 @@
# #
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from users.models import User
from ldap_sync.models import synchronise_user from ldap_sync.models import synchronise_user
from users.models import User
class Command(BaseCommand): class Command(BaseCommand):

View file

@ -1,18 +1,16 @@
import sys import sys
from django.db import models
from django.conf import settings
from django.dispatch import receiver
from django.contrib.auth.models import Group
import ldapdb.models import ldapdb.models
import ldapdb.models.fields import ldapdb.models.fields
from django.conf import settings
import users.signals from django.contrib.auth.models import Group
import users.models from django.db import models
from django.dispatch import receiver
import machines.models import machines.models
import users.models
import users.signals
class LdapUser(ldapdb.models.Model): class LdapUser(ldapdb.models.Model):
"""A class representing a LdapUser in LDAP, its LDAP conterpart. """A class representing a LdapUser in LDAP, its LDAP conterpart.
@ -110,12 +108,12 @@ def synchronise_user(sender, **kwargs):
* mac_refresh : Default `True`. When True, synchronise the list of mac addresses. * mac_refresh : Default `True`. When True, synchronise the list of mac addresses.
* group_refresh: Default `False`. When `True` synchronise the groups of the instance. * group_refresh: Default `False`. When `True` synchronise the groups of the instance.
""" """
base=kwargs.get('base', True) base = kwargs.get("base", True)
access_refresh=kwargs.get('access_refresh', True) access_refresh = kwargs.get("access_refresh", True)
mac_refresh=kwargs.get('mac_refresh', True ) mac_refresh = kwargs.get("mac_refresh", True)
group_refresh=kwargs.get('group_refresh', False) group_refresh = kwargs.get("group_refresh", False)
user=kwargs["instance"] user = kwargs["instance"]
if sys.version_info[0] >= 3 and ( if sys.version_info[0] >= 3 and (
user.state == user.STATE_ACTIVE user.state == user.STATE_ACTIVE
@ -136,9 +134,7 @@ def synchronise_user(sender, **kwargs):
user_ldap.dialupAccess = str(user.has_access()) user_ldap.dialupAccess = str(user.has_access())
user_ldap.home_directory = user.home_directory user_ldap.home_directory = user.home_directory
user_ldap.mail = user.get_mail user_ldap.mail = user.get_mail
user_ldap.given_name = ( user_ldap.given_name = user.surname.lower() + "_" + user.name.lower()[:3]
user.surname.lower() + "_" + user.name.lower()[:3]
)
user_ldap.gid = settings.LDAP["user_gid"] user_ldap.gid = settings.LDAP["user_gid"]
if "{SSHA}" in user.password or "{SMD5}" in user.password: if "{SSHA}" in user.password or "{SMD5}" in user.password:
# We remove the extra $ added at import from ldap # We remove the extra $ added at import from ldap
@ -169,9 +165,12 @@ def synchronise_user(sender, **kwargs):
# be part of the updated group (case of group removal) # be part of the updated group (case of group removal)
for group in Group.objects.all(): for group in Group.objects.all():
if hasattr(group, "listright"): if hasattr(group, "listright"):
synchronise_usergroup(users.models.ListRight, instance=group.listright) synchronise_usergroup(
users.models.ListRight, instance=group.listright
)
user_ldap.save() user_ldap.save()
@receiver(users.signals.remove, sender=users.models.User) @receiver(users.signals.remove, sender=users.models.User)
def remove_user(sender, **kwargs): def remove_user(sender, **kwargs):
user = kwargs["instance"] user = kwargs["instance"]
@ -181,6 +180,7 @@ def remove_user(sender, **kwargs):
except LdapUser.DoesNotExist: except LdapUser.DoesNotExist:
pass pass
@receiver(users.signals.remove_mass, sender=users.models.User) @receiver(users.signals.remove_mass, sender=users.models.User)
def remove_users(sender, **kwargs): def remove_users(sender, **kwargs):
queryset_users = kwargs["queryset"] queryset_users = kwargs["queryset"]
@ -217,6 +217,7 @@ class LdapUserGroup(ldapdb.models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
@receiver(users.signals.synchronise, sender=users.models.ListRight) @receiver(users.signals.synchronise, sender=users.models.ListRight)
def synchronise_usergroup(sender, **kwargs): def synchronise_usergroup(sender, **kwargs):
group = kwargs["instance"] group = kwargs["instance"]
@ -228,6 +229,7 @@ def synchronise_usergroup(sender, **kwargs):
group_ldap.members = [user.pseudo for user in group.user_set.all()] group_ldap.members = [user.pseudo for user in group.user_set.all()]
group_ldap.save() group_ldap.save()
@receiver(users.signals.remove, sender=users.models.ListRight) @receiver(users.signals.remove, sender=users.models.ListRight)
def remove_usergroup(sender, **kwargs): def remove_usergroup(sender, **kwargs):
group = kwargs["instance"] group = kwargs["instance"]
@ -238,7 +240,6 @@ def remove_usergroup(sender, **kwargs):
pass pass
class LdapServiceUser(ldapdb.models.Model): class LdapServiceUser(ldapdb.models.Model):
"""A class representing a ServiceUser in LDAP, its LDAP conterpart. """A class representing a ServiceUser in LDAP, its LDAP conterpart.
Synced from ServiceUser, with a copy of its attributes/fields into LDAP, Synced from ServiceUser, with a copy of its attributes/fields into LDAP,
@ -296,6 +297,7 @@ def synchronise_serviceuser(sender, **kwargs):
user_ldap.save() user_ldap.save()
synchronise_serviceuser_group(user) synchronise_serviceuser_group(user)
@receiver(users.signals.remove, sender=users.models.ServiceUser) @receiver(users.signals.remove, sender=users.models.ServiceUser)
def remove_serviceuser(sender, **kwargs): def remove_serviceuser(sender, **kwargs):
user = kwargs["instance"] user = kwargs["instance"]
@ -331,4 +333,3 @@ class LdapServiceUserGroup(ldapdb.models.Model):
def __str__(self): def __str__(self):
return self.name return self.name

View file

@ -1,4 +1,7 @@
from django.conf.urls import url from django.urls import path
from .import views
from . import views
app_name = "ldap_sync"
urlpatterns = [] urlpatterns = []

11
logs/apps.py Normal file
View file

@ -0,0 +1,11 @@
"""
Configuration of logs app.
"""
from django.apps import AppConfig
class LogsConfig(AppConfig):
"""Configuration of logs app."""
name = "logs"

View file

@ -21,13 +21,11 @@
"""The forms used by the machine search view""" """The forms used by the machine search view"""
import inspect
from django import forms from django import forms
from django.forms import Form from django.forms import Form
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from re2o.base import get_input_formats_help_text
from re2o.widgets import AutocompleteModelWidget
import inspect
# Import all models in which there are classes to be filtered on # Import all models in which there are classes to be filtered on
import cotisations.models import cotisations.models
@ -35,7 +33,8 @@ import machines.models
import preferences.models import preferences.models
import topologie.models import topologie.models
import users.models import users.models
from re2o.base import get_input_formats_help_text
from re2o.widgets import AutocompleteModelWidget
CHOICES_ACTION_TYPE = ( CHOICES_ACTION_TYPE = (
("users", _("Users")), ("users", _("Users")),

View file

@ -21,23 +21,17 @@
"""logs.models """logs.models
The models definitions for the logs app The models definitions for the logs app
""" """
from reversion.models import Version, Revision from django.apps import apps
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.db.models import Q from django.db.models import Q
from django.apps import apps from django.utils.translation import ugettext_lazy as _
from netaddr import EUI
from macaddress.fields import default_dialect from macaddress.fields import default_dialect
from netaddr import EUI
from reversion.models import Revision, Version
from machines.models import IpList from machines.models import Interface, IpList, Machine, MachineType
from machines.models import Interface from topologie.models import Port, Room
from machines.models import Machine from users.models import Adherent, Club, User
from machines.models import MachineType
from users.models import User
from users.models import Adherent
from users.models import Club
from topologie.models import Room
from topologie.models import Port
from .forms import classes_for_action_type from .forms import classes_for_action_type
@ -53,13 +47,12 @@ def make_version_filter(key, value):
# The lookup is done in a json string, so it has to be formated # The lookup is done in a json string, so it has to be formated
# based on the value's type (to add " or not) # based on the value's type (to add " or not)
if type(value) is str: if type(value) is str:
formatted_value = "\"{}\"".format(value) formatted_value = '"{}"'.format(value)
else: else:
formatted_value = str(value) formatted_value = str(value)
return ( return Q(serialized_data__contains='"{}": {},'.format(key, formatted_value)) | Q(
Q(serialized_data__contains='\"{}\": {},'.format(key, formatted_value)) serialized_data__contains='"{}": {}}}'.format(key, formatted_value)
| Q(serialized_data__contains='\"{}\": {}}}'.format(key, formatted_value))
) )
@ -67,6 +60,7 @@ def make_version_filter(key, value):
# Machine history search # # Machine history search #
############################ ############################
class MachineHistorySearchEvent: class MachineHistorySearchEvent:
def __init__(self, user, machine, interface, start=None, end=None): def __init__(self, user, machine, interface, start=None, end=None):
"""Initialise an instance of MachineHistorySearchEvent. """Initialise an instance of MachineHistorySearchEvent.
@ -113,7 +107,7 @@ class MachineHistorySearchEvent:
self.ipv4, self.ipv4,
self.start_date, self.start_date,
self.end_date, self.end_date,
self.comment or "No comment" self.comment or "No comment",
) )
@ -300,6 +294,7 @@ class MachineHistorySearch:
# Generic history classes # # Generic history classes #
############################ ############################
class RelatedHistory: class RelatedHistory:
def __init__(self, version): def __init__(self, version):
"""Initialise an instance of RelatedHistory. """Initialise an instance of RelatedHistory.
@ -317,10 +312,7 @@ class RelatedHistory:
self.name = "{}: {}".format(self.model_name.title(), self.name) self.name = "{}: {}".format(self.model_name.title(), self.name)
def __eq__(self, other): def __eq__(self, other):
return ( return self.model_name == other.model_name and self.object_id == other.object_id
self.model_name == other.model_name
and self.object_id == other.object_id
)
def __hash__(self): def __hash__(self):
return hash((self.model_name, self.object_id)) return hash((self.model_name, self.object_id))
@ -382,15 +374,11 @@ class HistoryEvent:
# Take into account keys that may exist in only one dict # Take into account keys that may exist in only one dict
if field in self.previous_version.field_dict: if field in self.previous_version.field_dict:
old_value = self._repr( old_value = self._repr(
field, field, self.previous_version.field_dict[field]
self.previous_version.field_dict[field]
) )
if field in self.version.field_dict: if field in self.version.field_dict:
new_value = self._repr( new_value = self._repr(field, self.version.field_dict[field])
field,
self.version.field_dict[field]
)
edits.append((field, old_value, new_value)) edits.append((field, old_value, new_value))
@ -487,6 +475,7 @@ class History:
# Revision history # # Revision history #
############################ ############################
class VersionAction(HistoryEvent): class VersionAction(HistoryEvent):
def __init__(self, version): def __init__(self, version):
self.version = version self.version = version
@ -533,15 +522,14 @@ class VersionAction(HistoryEvent):
""" """
model = self.object_type() model = self.object_type()
try: try:
query = ( query = make_version_filter("pk", self.object_id()) & Q(
make_version_filter("pk", self.object_id()) revision__date_created__lt=self.version.revision.date_created
& Q( )
revision__date_created__lt=self.version.revision.date_created return (
) Version.objects.get_for_model(model)
.filter(query)
.order_by("-revision__date_created")[0]
) )
return (Version.objects.get_for_model(model)
.filter(query)
.order_by("-revision__date_created")[0])
except Exception: except Exception:
return None return None
@ -648,6 +636,7 @@ class ActionsSearch:
# Class-specific history # # Class-specific history #
############################ ############################
class UserHistoryEvent(HistoryEvent): class UserHistoryEvent(HistoryEvent):
def _repr(self, name, value): def _repr(self, name, value):
"""Get the appropriate representation of the given field. """Get the appropriate representation of the given field.
@ -733,13 +722,15 @@ class UserHistoryEvent(HistoryEvent):
) )
def __hash__(self): def __hash__(self):
return hash((frozenset(self.edited_fields), self.date, self.performed_by, self.comment)) return hash(
(frozenset(self.edited_fields), self.date, self.performed_by, self.comment)
)
def __repr__(self): def __repr__(self):
return "{} edited fields {} ({})".format( return "{} edited fields {} ({})".format(
self.performed_by, self.performed_by,
self.edited_fields or "nothing", self.edited_fields or "nothing",
self.comment or "No comment" self.comment or "No comment",
) )
@ -762,9 +753,8 @@ class UserHistory(History):
# Try to find an Adherent object # Try to find an Adherent object
# If it exists, its id will be the same as the user's # If it exists, its id will be the same as the user's
adherents = ( adherents = Version.objects.get_for_model(Adherent).filter(
Version.objects.get_for_model(Adherent) make_version_filter("pk", user_id)
.filter(make_version_filter("pk", user_id))
) )
try: try:
obj = adherents[0] obj = adherents[0]
@ -774,9 +764,8 @@ class UserHistory(History):
# Fallback on a Club # Fallback on a Club
if obj is None: if obj is None:
clubs = ( clubs = Version.objects.get_for_model(Club).filter(
Version.objects.get_for_model(Club) make_version_filter("pk", user_id)
.filter(make_version_filter("pk", user_id))
) )
try: try:
@ -826,11 +815,7 @@ class UserHistory(History):
# Remove duplicates and sort # Remove duplicates and sort
self.events = list(dict.fromkeys(self.events)) self.events = list(dict.fromkeys(self.events))
return sorted( return sorted(self.events, key=lambda e: e.date, reverse=True)
self.events,
key=lambda e: e.date,
reverse=True
)
def _add_revision(self, version): def _add_revision(self, version):
"""Add a new revision to the chronological order. """Add a new revision to the chronological order.
@ -843,7 +828,7 @@ class UserHistory(History):
diff = self._compute_diff( diff = self._compute_diff(
version, version,
self._last_version, self._last_version,
ignoring=["last_login", "pwd_ntlm", "email_change_date"] ignoring=["last_login", "pwd_ntlm", "email_change_date"],
) )
# Ignore "empty" events like login # Ignore "empty" events like login
@ -973,7 +958,7 @@ HISTORY_CLASS_MAPPING = {
User: UserHistory, User: UserHistory,
Machine: MachineHistory, Machine: MachineHistory,
Interface: InterfaceHistory, Interface: InterfaceHistory,
"default": History "default": History,
} }

View file

@ -24,26 +24,32 @@ The defined URLs for the logs app. Included in re2o.urls.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url from django.urls import path
from . import views from . import views
app_name = "logs"
urlpatterns = [ urlpatterns = [
url(r"^$", views.index, name="index"), path("", views.index, name="index"),
url(r"^stats_logs$", views.stats_logs, name="stats-logs"), path("stats_logs", views.stats_logs, name="stats-logs"),
url( path(
r"^revert_action/(?P<revision_id>[0-9]+)$", "revert_action/<int:revision_id>",
views.revert_action, views.revert_action,
name="revert-action", name="revert-action",
), ),
url( path(
r"(?P<application>\w+)/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$", "<str:application>/<str:object_name>/<int:object_id>",
views.history, views.history,
name="history", name="history",
), ),
url(r"^stats_general/$", views.stats_general, name="stats-general"), path("stats_general", views.stats_general, name="stats-general"),
url(r"^stats_models/$", views.stats_models, name="stats-models"), path("stats_models", views.stats_models, name="stats-models"),
url(r"^stats_users/$", views.stats_users, name="stats-users"), path("stats_users", views.stats_users, name="stats-users"),
url(r"^stats_actions/$", views.stats_actions, name="stats-actions"), path("stats_actions", views.stats_actions, name="stats-actions"),
url(r"^stats_search_machine/$", views.stats_search_machine_history, name="stats-search-machine"), path(
"stats_search_machine",
views.stats_search_machine_history,
name="stats-search-machine",
),
] ]

View file

@ -38,84 +38,38 @@ objects for per model, number of actions per user etc.
from __future__ import unicode_literals from __future__ import unicode_literals
from django.urls import reverse from django.apps import apps
from django.shortcuts import render, redirect
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import Http404
from django.db.models import Count from django.db.models import Count
from django.apps import apps from django.http import Http404
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from reversion.models import ContentType, Revision, Version
from reversion.models import Revision from cotisations.models import (Article, Banque, Cotisation, Facture, Paiement,
from reversion.models import Version, ContentType Vente)
from machines.models import (SOA, Domain, Extension, Interface, IpList, IpType,
from users.models import ( Machine, MachineType, Mx, Nas, Ns,
User, OuverturePortList, Service, Vlan)
ServiceUser,
School,
ListRight,
ListShell,
Ban,
Whitelist,
Adherent,
Club,
)
from cotisations.models import Facture, Vente, Article, Banque, Paiement, Cotisation
from machines.models import (
Machine,
MachineType,
IpType,
Extension,
Interface,
Domain,
IpList,
OuverturePortList,
Service,
Vlan,
Nas,
SOA,
Mx,
Ns,
)
from topologie.models import (
Switch,
Port,
Room,
Stack,
ModelSwitch,
ConstructorSwitch,
AccessPoint,
)
from preferences.models import GeneralOption from preferences.models import GeneralOption
from re2o.acl import (acl_error_message, can_edit_history, can_view,
can_view_all, can_view_app)
from re2o.base import SortTable, re2o_paginator
from re2o.utils import (all_active_assigned_interfaces_count,
all_active_interfaces_count, all_adherent, all_baned,
all_has_access, all_whitelisted)
from re2o.views import form from re2o.views import form
from re2o.utils import ( from topologie.models import (AccessPoint, ConstructorSwitch, ModelSwitch,
all_whitelisted, Port, Room, Stack, Switch)
all_baned, from users.models import (Adherent, Ban, Club, ListRight, ListShell, School,
all_has_access, ServiceUser, User, Whitelist)
all_adherent,
all_active_assigned_interfaces_count,
all_active_interfaces_count,
)
from re2o.base import re2o_paginator, SortTable
from re2o.acl import (
can_view_all,
can_view_app,
can_edit_history,
can_view,
acl_error_message,
)
from .models import (
ActionsSearch,
RevisionAction,
MachineHistorySearch,
get_history_class,
)
from .forms import ActionsSearchForm, MachineHistorySearchForm
from .acl import can_view as can_view_logs from .acl import can_view as can_view_logs
from .forms import ActionsSearchForm, MachineHistorySearchForm
from .models import (ActionsSearch, MachineHistorySearch, RevisionAction,
get_history_class)
@login_required @login_required
@ -528,7 +482,11 @@ def stats_search_machine_history(request):
max_result = GeneralOption.get_cached_value("pagination_number") max_result = GeneralOption.get_cached_value("pagination_number")
events = re2o_paginator(request, events, max_result) events = re2o_paginator(request, events, max_result)
return render(request, "logs/machine_history.html", {"events": events},) return render(
request,
"logs/machine_history.html",
{"events": events},
)
return render( return render(
request, "logs/search_machine_history.html", {"history_form": history_form} request, "logs/search_machine_history.html", {"history_form": history_form}
) )

View file

@ -29,24 +29,10 @@ from __future__ import unicode_literals
from django.contrib import admin from django.contrib import admin
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
from .models import ( from .models import (SOA, DName, Domain, Extension, Interface, IpList, IpType,
Extension, Ipv6List, Machine, MachineType, Mx, Nas, Ns,
SOA, OuverturePort, OuverturePortList, Role, Service, Srv,
Mx, SshFp, Txt, Vlan)
Ns,
Vlan,
Txt,
DName,
Srv,
SshFp,
Nas,
Service,
Role,
OuverturePort,
Ipv6List,
OuverturePortList,
)
from .models import IpType, Machine, MachineType, Domain, IpList, Interface
class MachineAdmin(VersionAdmin): class MachineAdmin(VersionAdmin):

View file

@ -22,12 +22,12 @@
from rest_framework import serializers from rest_framework import serializers
import machines.models as machines import machines.models as machines
from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer from api.serializers import (NamespacedHIField, NamespacedHMSerializer,
NamespacedHRField)
class MachineSerializer(NamespacedHMSerializer): class MachineSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Machine` objects. """Serialize `machines.models.Machine` objects."""
"""
class Meta: class Meta:
model = machines.Machine model = machines.Machine
@ -35,8 +35,7 @@ class MachineSerializer(NamespacedHMSerializer):
class MachineTypeSerializer(NamespacedHMSerializer): class MachineTypeSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.MachineType` objects. """Serialize `machines.models.MachineType` objects."""
"""
class Meta: class Meta:
model = machines.MachineType model = machines.MachineType
@ -44,8 +43,7 @@ class MachineTypeSerializer(NamespacedHMSerializer):
class IpTypeSerializer(NamespacedHMSerializer): class IpTypeSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.IpType` objects. """Serialize `machines.models.IpType` objects."""
"""
class Meta: class Meta:
model = machines.IpType model = machines.IpType
@ -63,8 +61,7 @@ class IpTypeSerializer(NamespacedHMSerializer):
class VlanSerializer(NamespacedHMSerializer): class VlanSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Vlan` objects. """Serialize `machines.models.Vlan` objects."""
"""
class Meta: class Meta:
model = machines.Vlan model = machines.Vlan
@ -82,8 +79,7 @@ class VlanSerializer(NamespacedHMSerializer):
class NasSerializer(NamespacedHMSerializer): class NasSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Nas` objects. """Serialize `machines.models.Nas` objects."""
"""
class Meta: class Meta:
model = machines.Nas model = machines.Nas
@ -98,8 +94,7 @@ class NasSerializer(NamespacedHMSerializer):
class SOASerializer(NamespacedHMSerializer): class SOASerializer(NamespacedHMSerializer):
"""Serialize `machines.models.SOA` objects. """Serialize `machines.models.SOA` objects."""
"""
class Meta: class Meta:
model = machines.SOA model = machines.SOA
@ -107,8 +102,7 @@ class SOASerializer(NamespacedHMSerializer):
class ExtensionSerializer(NamespacedHMSerializer): class ExtensionSerializer(NamespacedHMSerializer):
"""Serialize machines.models.Extension objects. """Serialize machines.models.Extension objects."""
"""
class Meta: class Meta:
model = machines.Extension model = machines.Extension
@ -116,8 +110,7 @@ class ExtensionSerializer(NamespacedHMSerializer):
class MxSerializer(NamespacedHMSerializer): class MxSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Mx` objects. """Serialize `machines.models.Mx` objects."""
"""
class Meta: class Meta:
model = machines.Mx model = machines.Mx
@ -125,8 +118,7 @@ class MxSerializer(NamespacedHMSerializer):
class DNameSerializer(NamespacedHMSerializer): class DNameSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.DName` objects. """Serialize `machines.models.DName` objects."""
"""
class Meta: class Meta:
model = machines.DName model = machines.DName
@ -134,8 +126,7 @@ class DNameSerializer(NamespacedHMSerializer):
class NsSerializer(NamespacedHMSerializer): class NsSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Ns` objects. """Serialize `machines.models.Ns` objects."""
"""
class Meta: class Meta:
model = machines.Ns model = machines.Ns
@ -143,8 +134,7 @@ class NsSerializer(NamespacedHMSerializer):
class TxtSerializer(NamespacedHMSerializer): class TxtSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Txt` objects. """Serialize `machines.models.Txt` objects."""
"""
class Meta: class Meta:
model = machines.Txt model = machines.Txt
@ -152,8 +142,7 @@ class TxtSerializer(NamespacedHMSerializer):
class SrvSerializer(NamespacedHMSerializer): class SrvSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Srv` objects. """Serialize `machines.models.Srv` objects."""
"""
class Meta: class Meta:
model = machines.Srv model = machines.Srv
@ -171,8 +160,7 @@ class SrvSerializer(NamespacedHMSerializer):
class SshFpSerializer(NamespacedHMSerializer): class SshFpSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.SSHFP` objects. """Serialize `machines.models.SSHFP` objects."""
"""
class Meta: class Meta:
model = machines.SshFp model = machines.SshFp
@ -180,8 +168,7 @@ class SshFpSerializer(NamespacedHMSerializer):
class InterfaceSerializer(NamespacedHMSerializer): class InterfaceSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Interface` objects. """Serialize `machines.models.Interface` objects."""
"""
mac_address = serializers.CharField() mac_address = serializers.CharField()
active = serializers.BooleanField(source="is_active") active = serializers.BooleanField(source="is_active")
@ -201,8 +188,7 @@ class InterfaceSerializer(NamespacedHMSerializer):
class Ipv6ListSerializer(NamespacedHMSerializer): class Ipv6ListSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Ipv6List` objects. """Serialize `machines.models.Ipv6List` objects."""
"""
class Meta: class Meta:
model = machines.Ipv6List model = machines.Ipv6List
@ -210,8 +196,7 @@ class Ipv6ListSerializer(NamespacedHMSerializer):
class DomainSerializer(NamespacedHMSerializer): class DomainSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Domain` objects. """Serialize `machines.models.Domain` objects."""
"""
class Meta: class Meta:
model = machines.Domain model = machines.Domain
@ -219,8 +204,7 @@ class DomainSerializer(NamespacedHMSerializer):
class IpListSerializer(NamespacedHMSerializer): class IpListSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.IpList` objects. """Serialize `machines.models.IpList` objects."""
"""
class Meta: class Meta:
model = machines.IpList model = machines.IpList
@ -228,8 +212,7 @@ class IpListSerializer(NamespacedHMSerializer):
class ServiceSerializer(NamespacedHMSerializer): class ServiceSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Service` objects. """Serialize `machines.models.Service` objects."""
"""
class Meta: class Meta:
model = machines.Service model = machines.Service
@ -243,8 +226,7 @@ class ServiceSerializer(NamespacedHMSerializer):
class ServiceLinkSerializer(NamespacedHMSerializer): class ServiceLinkSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.Service_link` objects. """Serialize `machines.models.Service_link` objects."""
"""
class Meta: class Meta:
model = machines.Service_link model = machines.Service_link
@ -260,8 +242,7 @@ class ServiceLinkSerializer(NamespacedHMSerializer):
class OuverturePortListSerializer(NamespacedHMSerializer): class OuverturePortListSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.OuverturePortList` objects. """Serialize `machines.models.OuverturePortList` objects."""
"""
tcp_ports_in = NamespacedHRField( tcp_ports_in = NamespacedHRField(
view_name="ouvertureport-detail", many=True, read_only=True view_name="ouvertureport-detail", many=True, read_only=True
@ -289,8 +270,7 @@ class OuverturePortListSerializer(NamespacedHMSerializer):
class OuverturePortSerializer(NamespacedHMSerializer): class OuverturePortSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.OuverturePort` objects. """Serialize `machines.models.OuverturePort` objects."""
"""
class Meta: class Meta:
model = machines.OuverturePort model = machines.OuverturePort
@ -298,8 +278,7 @@ class OuverturePortSerializer(NamespacedHMSerializer):
class RoleSerializer(NamespacedHMSerializer): class RoleSerializer(NamespacedHMSerializer):
"""Serialize `machines.models.OuverturePort` objects. """Serialize `machines.models.OuverturePort` objects."""
"""
servers = InterfaceSerializer(read_only=True, many=True) servers = InterfaceSerializer(read_only=True, many=True)
@ -309,8 +288,7 @@ class RoleSerializer(NamespacedHMSerializer):
class ServiceRegenSerializer(NamespacedHMSerializer): class ServiceRegenSerializer(NamespacedHMSerializer):
"""Serialize the data about the services to regen. """Serialize the data about the services to regen."""
"""
hostname = serializers.CharField(source="server.domain.name", read_only=True) hostname = serializers.CharField(source="server.domain.name", read_only=True)
service_name = serializers.CharField(source="service.service_type", read_only=True) service_name = serializers.CharField(source="service.service_type", read_only=True)
@ -517,8 +495,7 @@ class DNAMERecordSerializer(serializers.ModelSerializer):
class DNSZonesSerializer(serializers.ModelSerializer): class DNSZonesSerializer(serializers.ModelSerializer):
"""Serialize the data about DNS Zones. """Serialize the data about DNS Zones."""
"""
soa = SOARecordSerializer() soa = SOARecordSerializer()
ns_records = NSRecordSerializer(many=True, source="ns_set") ns_records = NSRecordSerializer(many=True, source="ns_set")
@ -559,8 +536,7 @@ class DNSZonesSerializer(serializers.ModelSerializer):
class DNSReverseZonesSerializer(serializers.ModelSerializer): class DNSReverseZonesSerializer(serializers.ModelSerializer):
"""Serialize the data about DNS Zones. """Serialize the data about DNS Zones."""
"""
soa = SOARecordSerializer(source="extension.soa") soa = SOARecordSerializer(source="extension.soa")
extension = serializers.CharField(source="extension.name", read_only=True) extension = serializers.CharField(source="extension.name", read_only=True)

View file

@ -45,9 +45,8 @@ urls_viewset = [
(r"machines/ouvertureport", views.OuverturePortViewSet, None), (r"machines/ouvertureport", views.OuverturePortViewSet, None),
(r"machines/role", views.RoleViewSet, None), (r"machines/role", views.RoleViewSet, None),
(r"machines/services-regen", views.ServiceRegenViewSet, "serviceregen"), (r"machines/services-regen", views.ServiceRegenViewSet, "serviceregen"),
# Deprecated # Deprecated
(r"services/regen", views.ServiceRegenViewSet, "serviceregen") (r"services/regen", views.ServiceRegenViewSet, "serviceregen"),
] ]
urls_view = [ urls_view = [
@ -56,7 +55,6 @@ urls_view = [
(r"machines/firewall-interface-ports", views.InterfacePortsOpenView), (r"machines/firewall-interface-ports", views.InterfacePortsOpenView),
(r"machines/dns-zones", views.DNSZonesView), (r"machines/dns-zones", views.DNSZonesView),
(r"machines/dns-reverse-zones", views.DNSReverseZonesView), (r"machines/dns-reverse-zones", views.DNSReverseZonesView),
# Deprecated # Deprecated
(r"dhcp/hostmacip", views.HostMacIpView), (r"dhcp/hostmacip", views.HostMacIpView),
(r"firewall/subnet-ports", views.SubnetPortsOpenView), (r"firewall/subnet-ports", views.SubnetPortsOpenView),

View file

@ -19,159 +19,142 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from rest_framework import viewsets, generics from rest_framework import generics, viewsets
from . import serializers
import machines.models as machines import machines.models as machines
from re2o.utils import all_active_interfaces from re2o.utils import all_active_interfaces
from . import serializers
class MachineViewSet(viewsets.ReadOnlyModelViewSet): class MachineViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Machine` objects. """Exposes list and details of `machines.models.Machine` objects."""
"""
queryset = machines.Machine.objects.all() queryset = machines.Machine.objects.all()
serializer_class = serializers.MachineSerializer serializer_class = serializers.MachineSerializer
class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet): class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.MachineType` objects. """Exposes list and details of `machines.models.MachineType` objects."""
"""
queryset = machines.MachineType.objects.all() queryset = machines.MachineType.objects.all()
serializer_class = serializers.MachineTypeSerializer serializer_class = serializers.MachineTypeSerializer
class IpTypeViewSet(viewsets.ReadOnlyModelViewSet): class IpTypeViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.IpType` objects. """Exposes list and details of `machines.models.IpType` objects."""
"""
queryset = machines.IpType.objects.all() queryset = machines.IpType.objects.all()
serializer_class = serializers.IpTypeSerializer serializer_class = serializers.IpTypeSerializer
class VlanViewSet(viewsets.ReadOnlyModelViewSet): class VlanViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Vlan` objects. """Exposes list and details of `machines.models.Vlan` objects."""
"""
queryset = machines.Vlan.objects.all() queryset = machines.Vlan.objects.all()
serializer_class = serializers.VlanSerializer serializer_class = serializers.VlanSerializer
class NasViewSet(viewsets.ReadOnlyModelViewSet): class NasViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Nas` objects. """Exposes list and details of `machines.models.Nas` objects."""
"""
queryset = machines.Nas.objects.all() queryset = machines.Nas.objects.all()
serializer_class = serializers.NasSerializer serializer_class = serializers.NasSerializer
class SOAViewSet(viewsets.ReadOnlyModelViewSet): class SOAViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.SOA` objects. """Exposes list and details of `machines.models.SOA` objects."""
"""
queryset = machines.SOA.objects.all() queryset = machines.SOA.objects.all()
serializer_class = serializers.SOASerializer serializer_class = serializers.SOASerializer
class ExtensionViewSet(viewsets.ReadOnlyModelViewSet): class ExtensionViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Extension` objects. """Exposes list and details of `machines.models.Extension` objects."""
"""
queryset = machines.Extension.objects.all() queryset = machines.Extension.objects.all()
serializer_class = serializers.ExtensionSerializer serializer_class = serializers.ExtensionSerializer
class MxViewSet(viewsets.ReadOnlyModelViewSet): class MxViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Mx` objects. """Exposes list and details of `machines.models.Mx` objects."""
"""
queryset = machines.Mx.objects.all() queryset = machines.Mx.objects.all()
serializer_class = serializers.MxSerializer serializer_class = serializers.MxSerializer
class NsViewSet(viewsets.ReadOnlyModelViewSet): class NsViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Ns` objects. """Exposes list and details of `machines.models.Ns` objects."""
"""
queryset = machines.Ns.objects.all() queryset = machines.Ns.objects.all()
serializer_class = serializers.NsSerializer serializer_class = serializers.NsSerializer
class TxtViewSet(viewsets.ReadOnlyModelViewSet): class TxtViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Txt` objects. """Exposes list and details of `machines.models.Txt` objects."""
"""
queryset = machines.Txt.objects.all() queryset = machines.Txt.objects.all()
serializer_class = serializers.TxtSerializer serializer_class = serializers.TxtSerializer
class DNameViewSet(viewsets.ReadOnlyModelViewSet): class DNameViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.DName` objects. """Exposes list and details of `machines.models.DName` objects."""
"""
queryset = machines.DName.objects.all() queryset = machines.DName.objects.all()
serializer_class = serializers.DNameSerializer serializer_class = serializers.DNameSerializer
class SrvViewSet(viewsets.ReadOnlyModelViewSet): class SrvViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Srv` objects. """Exposes list and details of `machines.models.Srv` objects."""
"""
queryset = machines.Srv.objects.all() queryset = machines.Srv.objects.all()
serializer_class = serializers.SrvSerializer serializer_class = serializers.SrvSerializer
class SshFpViewSet(viewsets.ReadOnlyModelViewSet): class SshFpViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.SshFp` objects. """Exposes list and details of `machines.models.SshFp` objects."""
"""
queryset = machines.SshFp.objects.all() queryset = machines.SshFp.objects.all()
serializer_class = serializers.SshFpSerializer serializer_class = serializers.SshFpSerializer
class InterfaceViewSet(viewsets.ReadOnlyModelViewSet): class InterfaceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Interface` objects. """Exposes list and details of `machines.models.Interface` objects."""
"""
queryset = machines.Interface.objects.all() queryset = machines.Interface.objects.all()
serializer_class = serializers.InterfaceSerializer serializer_class = serializers.InterfaceSerializer
class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet): class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Ipv6List` objects. """Exposes list and details of `machines.models.Ipv6List` objects."""
"""
queryset = machines.Ipv6List.objects.all() queryset = machines.Ipv6List.objects.all()
serializer_class = serializers.Ipv6ListSerializer serializer_class = serializers.Ipv6ListSerializer
class DomainViewSet(viewsets.ReadOnlyModelViewSet): class DomainViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Domain` objects. """Exposes list and details of `machines.models.Domain` objects."""
"""
queryset = machines.Domain.objects.all() queryset = machines.Domain.objects.all()
serializer_class = serializers.DomainSerializer serializer_class = serializers.DomainSerializer
class IpListViewSet(viewsets.ReadOnlyModelViewSet): class IpListViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.IpList` objects. """Exposes list and details of `machines.models.IpList` objects."""
"""
queryset = machines.IpList.objects.all() queryset = machines.IpList.objects.all()
serializer_class = serializers.IpListSerializer serializer_class = serializers.IpListSerializer
class ServiceViewSet(viewsets.ReadOnlyModelViewSet): class ServiceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Service` objects. """Exposes list and details of `machines.models.Service` objects."""
"""
queryset = machines.Service.objects.all() queryset = machines.Service.objects.all()
serializer_class = serializers.ServiceSerializer serializer_class = serializers.ServiceSerializer
class ServiceLinkViewSet(viewsets.ReadOnlyModelViewSet): class ServiceLinkViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Service_link` objects. """Exposes list and details of `machines.models.Service_link` objects."""
"""
queryset = machines.Service_link.objects.all() queryset = machines.Service_link.objects.all()
serializer_class = serializers.ServiceLinkSerializer serializer_class = serializers.ServiceLinkSerializer
@ -187,24 +170,21 @@ class OuverturePortListViewSet(viewsets.ReadOnlyModelViewSet):
class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet): class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.OuverturePort` objects. """Exposes list and details of `machines.models.OuverturePort` objects."""
"""
queryset = machines.OuverturePort.objects.all() queryset = machines.OuverturePort.objects.all()
serializer_class = serializers.OuverturePortSerializer serializer_class = serializers.OuverturePortSerializer
class RoleViewSet(viewsets.ReadOnlyModelViewSet): class RoleViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Machine` objects. """Exposes list and details of `machines.models.Machine` objects."""
"""
queryset = machines.Role.objects.all() queryset = machines.Role.objects.all()
serializer_class = serializers.RoleSerializer serializer_class = serializers.RoleSerializer
class ServiceRegenViewSet(viewsets.ModelViewSet): class ServiceRegenViewSet(viewsets.ModelViewSet):
"""Exposes list and details of the services to regen """Exposes list and details of the services to regen"""
"""
serializer_class = serializers.ServiceRegenSerializer serializer_class = serializers.ServiceRegenSerializer
@ -238,6 +218,7 @@ class InterfacePortsOpenView(generics.ListAPIView):
queryset = machines.Interface.objects.filter(port_lists__isnull=False).distinct() queryset = machines.Interface.objects.filter(port_lists__isnull=False).distinct()
serializer_class = serializers.InterfacePortsOpenSerializer serializer_class = serializers.InterfacePortsOpenSerializer
class DNSZonesView(generics.ListAPIView): class DNSZonesView(generics.ListAPIView):
"""Exposes the detailed information about each extension (hostnames, """Exposes the detailed information about each extension (hostnames,
IPs, DNS records, etc.) in order to build the DNS zone files. IPs, DNS records, etc.) in order to build the DNS zone files.

11
machines/apps.py Normal file
View file

@ -0,0 +1,11 @@
"""
Configuration of machines app.
"""
from django.apps import AppConfig
class MachinesConfig(AppConfig):
"""Configuration of machines app."""
name = "machines"

View file

@ -36,37 +36,17 @@ Forms to create, edit and delete:
from __future__ import unicode_literals from __future__ import unicode_literals
from django import forms from django import forms
from django.forms import ModelForm, Form from django.forms import Form, ModelForm
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from re2o.field_permissions import FieldPermissionFormMixin from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin from re2o.mixins import FormRevMixin
from re2o.widgets import ( from re2o.widgets import (AutocompleteModelWidget,
AutocompleteModelWidget, AutocompleteMultipleModelWidget)
AutocompleteMultipleModelWidget,
) from .models import (SOA, DName, Domain, Extension, Interface, IpList, IpType,
from .models import ( Ipv6List, Machine, MachineType, Mx, Nas, Ns,
Domain, OuverturePortList, Role, Service, Srv, SshFp, Txt, Vlan)
Machine,
Interface,
IpList,
MachineType,
Extension,
SOA,
Mx,
Txt,
DName,
Ns,
Role,
Service,
Vlan,
Srv,
SshFp,
Nas,
IpType,
OuverturePortList,
Ipv6List,
)
class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
@ -248,7 +228,9 @@ class IpTypeForm(FormRevMixin, ModelForm):
fields = "__all__" fields = "__all__"
widgets = { widgets = {
"vlan": AutocompleteModelWidget(url="/machines/vlan-autocomplete"), "vlan": AutocompleteModelWidget(url="/machines/vlan-autocomplete"),
"extension": AutocompleteModelWidget(url="/machines/extension-autocomplete"), "extension": AutocompleteModelWidget(
url="/machines/extension-autocomplete"
),
"ouverture_ports": AutocompleteModelWidget( "ouverture_ports": AutocompleteModelWidget(
url="/machines/ouvertureportlist-autocomplete" url="/machines/ouvertureportlist-autocomplete"
), ),
@ -525,7 +507,9 @@ class SrvForm(FormRevMixin, ModelForm):
model = Srv model = Srv
fields = "__all__" fields = "__all__"
widgets = { widgets = {
"extension": AutocompleteModelWidget(url="/machines/extension-autocomplete"), "extension": AutocompleteModelWidget(
url="/machines/extension-autocomplete"
),
"target": AutocompleteModelWidget(url="/machines/domain-autocomplete"), "target": AutocompleteModelWidget(url="/machines/domain-autocomplete"),
} }

View file

@ -1098,7 +1098,7 @@ class Migration(migrations.Migration):
( (
"cname", "cname",
models.ForeignKey( models.ForeignKey(
"self", null=True, blank=True, related_name="related_domain" "self", null=True, blank=True, related_name="related_domain", on_delete=models.CASCADE
), ),
), ),
( (

View file

@ -0,0 +1,108 @@
# Generated by Django 2.2.18 on 2021-02-08 17:27
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('machines', '0002_foreign_keys'),
]
operations = [
migrations.AlterModelOptions(
name='dname',
options={'verbose_name': 'DNAME record', 'verbose_name_plural': 'DNAME records'},
),
migrations.AlterModelOptions(
name='domain',
options={'permissions': (('change_ttl', 'Can change the TTL of a domain object'),), 'verbose_name': 'domain', 'verbose_name_plural': 'domains'},
),
migrations.AlterModelOptions(
name='extension',
options={'permissions': (('use_all_extension', 'Can use all extensions'),), 'verbose_name': 'DNS extension', 'verbose_name_plural': 'DNS extensions'},
),
migrations.AlterModelOptions(
name='interface',
options={'permissions': (('change_interface_machine', 'Can change the owner of an interface'),), 'verbose_name': 'interface', 'verbose_name_plural': 'interfaces'},
),
migrations.AlterModelOptions(
name='iplist',
options={'verbose_name': 'IPv4 addresses list', 'verbose_name_plural': 'IPv4 addresses lists'},
),
migrations.AlterModelOptions(
name='iptype',
options={'permissions': (('use_all_iptype', 'Can use all IP types'),), 'verbose_name': 'IP type', 'verbose_name_plural': 'IP types'},
),
migrations.AlterModelOptions(
name='ipv6list',
options={'permissions': (('change_ipv6list_slaac_ip', 'Can change the SLAAC value of an IPv6 addresses list'),), 'verbose_name': 'IPv6 addresses list', 'verbose_name_plural': 'IPv6 addresses lists'},
),
migrations.AlterModelOptions(
name='machine',
options={'permissions': (('change_machine_user', 'Can change the user of a machine'),), 'verbose_name': 'machine', 'verbose_name_plural': 'machines'},
),
migrations.AlterModelOptions(
name='machinetype',
options={'permissions': (('use_all_machinetype', 'Can use all machine types'),), 'verbose_name': 'machine type', 'verbose_name_plural': 'machine types'},
),
migrations.AlterModelOptions(
name='mx',
options={'verbose_name': 'MX record', 'verbose_name_plural': 'MX records'},
),
migrations.AlterModelOptions(
name='nas',
options={'verbose_name': 'NAS device', 'verbose_name_plural': 'NAS devices'},
),
migrations.AlterModelOptions(
name='ns',
options={'verbose_name': 'NS record', 'verbose_name_plural': 'NS records'},
),
migrations.AlterModelOptions(
name='ouvertureportlist',
options={'verbose_name': 'ports opening list', 'verbose_name_plural': 'ports opening lists'},
),
migrations.AlterModelOptions(
name='role',
options={'verbose_name': 'server role', 'verbose_name_plural': 'server roles'},
),
migrations.AlterModelOptions(
name='service',
options={'verbose_name': 'service to generate (DHCP, DNS, ...)', 'verbose_name_plural': 'services to generate (DHCP, DNS, ...)'},
),
migrations.AlterModelOptions(
name='service_link',
options={'verbose_name': 'link between service and server', 'verbose_name_plural': 'links between service and server'},
),
migrations.AlterModelOptions(
name='soa',
options={'verbose_name': 'SOA record', 'verbose_name_plural': 'SOA records'},
),
migrations.AlterModelOptions(
name='srv',
options={'verbose_name': 'SRV record', 'verbose_name_plural': 'SRV records'},
),
migrations.AlterModelOptions(
name='sshfp',
options={'verbose_name': 'SSHFP record', 'verbose_name_plural': 'SSHFP records'},
),
migrations.AlterModelOptions(
name='txt',
options={'verbose_name': 'TXT record', 'verbose_name_plural': 'TXT records'},
),
migrations.AlterModelOptions(
name='vlan',
options={'verbose_name': 'VLAN', 'verbose_name_plural': 'VLANs'},
),
migrations.AlterField(
model_name='domain',
name='cname',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='related_domain', to='machines.Domain'),
),
migrations.AlterField(
model_name='iptype',
name='ouverture_ports',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.OuverturePortList'),
),
]

View file

@ -34,6 +34,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="interface", model_name="interface",
name="machine", name="machine",
field=models.ForeignKey(to="machines.Machine"), field=models.ForeignKey(to="machines.Machine", on_delete=models.CASCADE),
) )
] ]

View file

@ -51,7 +51,7 @@ class Migration(migrations.Migration):
unique=True, unique=True,
), ),
), ),
("interface_parent", models.ForeignKey(to="machines.Interface")), ("interface_parent", models.ForeignKey(to="machines.Interface", on_delete=models.CASCADE)),
], ],
) )
] ]

View file

@ -35,7 +35,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="domain", model_name="domain",
name="interface_parent", name="interface_parent",
field=models.ForeignKey(to="machines.Interface", null=True, blank=True), field=models.ForeignKey(to="machines.Interface", null=True, blank=True, on_delete=models.CASCADE),
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name="domain", unique_together=set([("name", "extension")]) name="domain", unique_together=set([("name", "extension")])

View file

@ -39,6 +39,7 @@ class Migration(migrations.Migration):
null=True, null=True,
to="machines.Domain", to="machines.Domain",
blank=True, blank=True,
on_delete=models.CASCADE
), ),
) )
] ]

View file

@ -39,6 +39,7 @@ class Migration(migrations.Migration):
to="machines.Domain", to="machines.Domain",
related_name="related_domain", related_name="related_domain",
blank=True, blank=True,
on_delete=models.CASCADE,
), ),
) )
] ]

View file

@ -34,6 +34,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="domain", model_name="domain",
name="interface_parent", name="interface_parent",
field=models.OneToOneField(blank=True, null=True, to="machines.Interface"), field=models.OneToOneField(blank=True, null=True, to="machines.Interface", on_delete=models.CASCADE),
) )
] ]

View file

@ -35,26 +35,18 @@ from ipaddress import IPv6Address
from itertools import chain from itertools import chain
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models, transaction
from django.db.models import Q from django.db.models import Q
from django.db.models.signals import post_save, post_delete from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.forms import ValidationError from django.forms import ValidationError
from django.utils import timezone from django.utils import timezone
from django.db import transaction
from reversion import revisions as reversion
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from macaddress.fields import MACAddressField, default_dialect from macaddress.fields import MACAddressField, default_dialect
from netaddr import ( from netaddr import (EUI, IPAddress, IPNetwork, IPRange, IPSet,
mac_bare, NotRegisteredError, mac_bare)
EUI, from reversion import revisions as reversion
NotRegisteredError,
IPSet,
IPRange,
IPNetwork,
IPAddress,
)
import preferences.models import preferences.models
import users.models import users.models
@ -79,10 +71,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, AclMixin, models.Model):
active = models.BooleanField(default=True) active = models.BooleanField(default=True)
class Meta: class Meta:
permissions = ( permissions = (("change_machine_user", _("Can change the user of a machine")),)
("view_machine", _("Can view a machine object")),
("change_machine_user", _("Can change the user of a machine")),
)
verbose_name = _("machine") verbose_name = _("machine")
verbose_name_plural = _("machines") verbose_name_plural = _("machines")
@ -343,10 +332,7 @@ class MachineType(RevMixin, AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = ( permissions = (("use_all_machinetype", _("Can use all machine types")),)
("view_machinetype", _("Can view a machine type object")),
("use_all_machinetype", _("Can use all machine types")),
)
verbose_name = _("machine type") verbose_name = _("machine type")
verbose_name_plural = _("machine types") verbose_name_plural = _("machine types")
@ -358,7 +344,9 @@ class MachineType(RevMixin, AclMixin, models.Model):
"""Update domains extension with the extension of interface_parent. Called after update of an ip_type or a machine_type object. Exceptions are handled in the views. """Update domains extension with the extension of interface_parent. Called after update of an ip_type or a machine_type object. Exceptions are handled in the views.
(Calling domain.clear() for all domains could take several minutes) (Calling domain.clear() for all domains could take several minutes)
""" """
Domain.objects.filter(interface_parent__machine_type=self).update(extension=self.ip_type.extension) Domain.objects.filter(interface_parent__machine_type=self).update(
extension=self.ip_type.extension
)
@staticmethod @staticmethod
def can_use_all(user_request, *_args, **_kwargs): def can_use_all(user_request, *_args, **_kwargs):
@ -391,20 +379,13 @@ class MachineType(RevMixin, AclMixin, models.Model):
""" """
can, _message, _group = cls.can_use_all(user_request) can, _message, _group = cls.can_use_all(user_request)
if can: if can:
return ( return (True, None, None, cls.objects.all())
True,
None,
None,
cls.objects.all()
)
else: else:
return ( return (
True, True,
_("You don't have the right to use all machine types."), _("You don't have the right to use all machine types."),
("machines.use_all_machinetype",), ("machines.use_all_machinetype",),
cls.objects.filter( cls.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False)),
ip_type__in=IpType.objects.filter(need_infra=False)
),
) )
def __str__(self): def __str__(self):
@ -457,13 +438,12 @@ class IpType(RevMixin, AclMixin, models.Model):
default=False, help_text=_("Enable reverse DNS for IPv6.") default=False, help_text=_("Enable reverse DNS for IPv6.")
) )
vlan = models.ForeignKey("Vlan", on_delete=models.PROTECT, blank=True, null=True) vlan = models.ForeignKey("Vlan", on_delete=models.PROTECT, blank=True, null=True)
ouverture_ports = models.ForeignKey("OuverturePortList", blank=True, null=True) ouverture_ports = models.ForeignKey(
"OuverturePortList", blank=True, null=True, on_delete=models.PROTECT
)
class Meta: class Meta:
permissions = ( permissions = (("use_all_iptype", _("Can use all IP types")),)
("view_iptype", _("Can view an IP type object")),
("use_all_iptype", _("Can use all IP types")),
)
verbose_name = _("IP type") verbose_name = _("IP type")
verbose_name_plural = _("IP types") verbose_name_plural = _("IP types")
@ -729,7 +709,6 @@ class Vlan(RevMixin, AclMixin, models.Model):
mld = models.BooleanField(default=False, help_text=_("v6 multicast management.")) mld = models.BooleanField(default=False, help_text=_("v6 multicast management."))
class Meta: class Meta:
permissions = (("view_vlan", _("Can view a VLAN object")),)
verbose_name = _("VLAN") verbose_name = _("VLAN")
verbose_name_plural = _("VLANs") verbose_name_plural = _("VLANs")
@ -765,7 +744,6 @@ class Nas(RevMixin, AclMixin, models.Model):
autocapture_mac = models.BooleanField(default=False) autocapture_mac = models.BooleanField(default=False)
class Meta: class Meta:
permissions = (("view_nas", _("Can view a NAS device object")),)
verbose_name = _("NAS device") verbose_name = _("NAS device")
verbose_name_plural = _("NAS devices") verbose_name_plural = _("NAS devices")
@ -819,7 +797,6 @@ class SOA(RevMixin, AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = (("view_soa", _("Can view an SOA record object")),)
verbose_name = _("SOA record") verbose_name = _("SOA record")
verbose_name_plural = _("SOA records") verbose_name_plural = _("SOA records")
@ -903,10 +880,7 @@ class Extension(RevMixin, AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = ( permissions = (("use_all_extension", _("Can use all extensions")),)
("view_extension", _("Can view an extension object")),
("use_all_extension", _("Can use all extensions")),
)
verbose_name = _("DNS extension") verbose_name = _("DNS extension")
verbose_name_plural = _("DNS extensions") verbose_name_plural = _("DNS extensions")
@ -994,12 +968,7 @@ class Extension(RevMixin, AclMixin, models.Model):
""" """
can, _message, _group = cls.can_use_all(user_request) can, _message, _group = cls.can_use_all(user_request)
if can: if can:
return ( return (True, None, None, cls.objects.all())
True,
None,
None,
cls.objects.all()
)
else: else:
return ( return (
True, True,
@ -1037,14 +1006,12 @@ class Mx(RevMixin, AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = (("view_mx", _("Can view an MX record object")),)
verbose_name = _("MX record") verbose_name = _("MX record")
verbose_name_plural = _("MX records") verbose_name_plural = _("MX records")
@cached_property @cached_property
def dns_entry(self): def dns_entry(self):
"""Get the complete DNS entry of the MX record, to put in zone files. """Get the complete DNS entry of the MX record, to put in zone files."""
"""
return "@ IN MX {prior} {name}".format( return "@ IN MX {prior} {name}".format(
prior=str(self.priority).ljust(3), name=str(self.name) prior=str(self.priority).ljust(3), name=str(self.name)
) )
@ -1069,14 +1036,12 @@ class Ns(RevMixin, AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = (("view_ns", _("Can view an NS record object")),)
verbose_name = _("NS record") verbose_name = _("NS record")
verbose_name_plural = _("NS records") verbose_name_plural = _("NS records")
@cached_property @cached_property
def dns_entry(self): def dns_entry(self):
"""Get the complete DNS entry of the NS record, to put in zone files. """Get the complete DNS entry of the NS record, to put in zone files."""
"""
return "@ IN NS " + str(self.ns) return "@ IN NS " + str(self.ns)
def __str__(self): def __str__(self):
@ -1101,7 +1066,6 @@ class Txt(RevMixin, AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = (("view_txt", _("Can view a TXT record object")),)
verbose_name = _("TXT record") verbose_name = _("TXT record")
verbose_name_plural = _("TXT records") verbose_name_plural = _("TXT records")
@ -1110,8 +1074,7 @@ class Txt(RevMixin, AclMixin, models.Model):
@cached_property @cached_property
def dns_entry(self): def dns_entry(self):
"""Get the complete DNS entry of the TXT record, to put in zone files. """Get the complete DNS entry of the TXT record, to put in zone files."""
"""
return str(self.field1).ljust(15) + " IN TXT " + str(self.field2) return str(self.field1).ljust(15) + " IN TXT " + str(self.field2)
@ -1131,7 +1094,6 @@ class DName(RevMixin, AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = (("view_dname", _("Can view a DNAME record object")),)
verbose_name = _("DNAME record") verbose_name = _("DNAME record")
verbose_name_plural = _("DNAME records") verbose_name_plural = _("DNAME records")
@ -1140,8 +1102,7 @@ class DName(RevMixin, AclMixin, models.Model):
@cached_property @cached_property
def dns_entry(self): def dns_entry(self):
"""Get the complete DNS entry of the TXT record, to put in zone files. """Get the complete DNS entry of the TXT record, to put in zone files."""
"""
return str(self.alias).ljust(15) + " IN DNAME " + str(self.zone) return str(self.alias).ljust(15) + " IN DNAME " + str(self.zone)
@ -1195,7 +1156,6 @@ class Srv(RevMixin, AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = (("view_srv", _("Can view an SRV record object")),)
verbose_name = _("SRV record") verbose_name = _("SRV record")
verbose_name_plural = _("SRV records") verbose_name_plural = _("SRV records")
@ -1216,8 +1176,7 @@ class Srv(RevMixin, AclMixin, models.Model):
@cached_property @cached_property
def dns_entry(self): def dns_entry(self):
"""Get the complete DNS entry of the SRV record, to put in zone files. """Get the complete DNS entry of the SRV record, to put in zone files."""
"""
return ( return (
str(self.service) str(self.service)
+ "._" + "._"
@ -1285,7 +1244,6 @@ class SshFp(RevMixin, AclMixin, models.Model):
} }
class Meta: class Meta:
permissions = (("view_sshfp", _("Can view an SSHFP record object")),)
verbose_name = _("SSHFP record") verbose_name = _("SSHFP record")
verbose_name_plural = _("SSHFP records") verbose_name_plural = _("SSHFP records")
@ -1350,7 +1308,6 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
class Meta: class Meta:
permissions = ( permissions = (
("view_interface", _("Can view an interface object")),
("change_interface_machine", _("Can change the owner of an interface")), ("change_interface_machine", _("Can change the owner of an interface")),
) )
verbose_name = _("interface") verbose_name = _("interface")
@ -1401,8 +1358,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
return vendor return vendor
def sync_ipv6_dhcpv6(self): def sync_ipv6_dhcpv6(self):
"""Assign an IPv6 address by DHCPv6, computed from the interface's ID. """Assign an IPv6 address by DHCPv6, computed from the interface's ID."""
"""
ipv6_dhcpv6 = self.gen_ipv6_dhcpv6 ipv6_dhcpv6 = self.gen_ipv6_dhcpv6
if not ipv6_dhcpv6: if not ipv6_dhcpv6:
return return
@ -1428,8 +1384,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
ipv6_object.save() ipv6_object.save()
def sync_ipv6(self): def sync_ipv6(self):
"""Create and update the IPv6 addresses according to the IPv6 mode set. """Create and update the IPv6 addresses according to the IPv6 mode set."""
"""
if preferences.models.OptionalMachine.get_cached_value("ipv6_mode") == "SLAAC": if preferences.models.OptionalMachine.get_cached_value("ipv6_mode") == "SLAAC":
self.sync_ipv6_slaac() self.sync_ipv6_slaac()
elif ( elif (
@ -1595,8 +1550,10 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
_("You don't have the right to add a machine."), _("You don't have the right to add a machine."),
("machines.add_interface",), ("machines.add_interface",),
) )
max_lambdauser_interfaces = preferences.models.OptionalMachine.get_cached_value( max_lambdauser_interfaces = (
"max_lambdauser_interfaces" preferences.models.OptionalMachine.get_cached_value(
"max_lambdauser_interfaces"
)
) )
if machine.user != user_request: if machine.user != user_request:
return ( return (
@ -1735,13 +1692,11 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
) )
slaac_ip = models.BooleanField(default=False) slaac_ip = models.BooleanField(default=False)
active = models.BooleanField( active = models.BooleanField(
default=True, default=True, help_text=_("If false,the DNS will not provide this ip.")
help_text=_("If false,the DNS will not provide this ip.")
) )
class Meta: class Meta:
permissions = ( permissions = (
("view_ipv6list", _("Can view an IPv6 addresses list object")),
( (
"change_ipv6list_slaac_ip", "change_ipv6list_slaac_ip",
_("Can change the SLAAC value of an IPv6 addresses list"), _("Can change the SLAAC value of an IPv6 addresses list"),
@ -1871,8 +1826,9 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
def check_and_replace_prefix(self, prefix=None): def check_and_replace_prefix(self, prefix=None):
"""Check if the IPv6 prefix is correct and update it if not.""" """Check if the IPv6 prefix is correct and update it if not."""
prefix_v6 = prefix or self.interface.machine_type.ip_type.prefix_v6.encode().decode( prefix_v6 = (
"utf-8" prefix
or self.interface.machine_type.ip_type.prefix_v6.encode().decode("utf-8")
) )
if not prefix_v6: if not prefix_v6:
return return
@ -1945,7 +1901,11 @@ class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
) )
extension = models.ForeignKey("Extension", on_delete=models.PROTECT) extension = models.ForeignKey("Extension", on_delete=models.PROTECT)
cname = models.ForeignKey( cname = models.ForeignKey(
"self", null=True, blank=True, related_name="related_domain" "self",
null=True,
blank=True,
related_name="related_domain",
on_delete=models.PROTECT,
) )
ttl = models.PositiveIntegerField( ttl = models.PositiveIntegerField(
verbose_name=_("Time To Live (TTL)"), verbose_name=_("Time To Live (TTL)"),
@ -1955,10 +1915,7 @@ class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
class Meta: class Meta:
unique_together = (("name", "extension"),) unique_together = (("name", "extension"),)
permissions = ( permissions = (("change_ttl", _("Can change the TTL of a domain object")),)
("view_domain", _("Can view a domain object")),
("change_ttl", _("Can change the TTL of a domain object")),
)
verbose_name = _("domain") verbose_name = _("domain")
verbose_name_plural = _("domains") verbose_name_plural = _("domains")
@ -2053,8 +2010,10 @@ class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
except Interface.DoesNotExist: except Interface.DoesNotExist:
return False, _("Nonexistent interface."), None return False, _("Nonexistent interface."), None
if not user_request.has_perm("machines.add_domain"): if not user_request.has_perm("machines.add_domain"):
max_lambdauser_aliases = preferences.models.OptionalMachine.get_cached_value( max_lambdauser_aliases = (
"max_lambdauser_aliases" preferences.models.OptionalMachine.get_cached_value(
"max_lambdauser_aliases"
)
) )
if interface.machine.user != user_request: if interface.machine.user != user_request:
return ( return (
@ -2184,14 +2143,12 @@ class IpList(RevMixin, AclMixin, models.Model):
ip_type = models.ForeignKey("IpType", on_delete=models.CASCADE) ip_type = models.ForeignKey("IpType", on_delete=models.CASCADE)
class Meta: class Meta:
permissions = (("view_iplist", _("Can view an IPv4 addresses list object")),)
verbose_name = _("IPv4 addresses list") verbose_name = _("IPv4 addresses list")
verbose_name_plural = _("IPv4 addresses lists") verbose_name_plural = _("IPv4 addresses lists")
@cached_property @cached_property
def need_infra(self): def need_infra(self):
"""Check if the 'infra' right is required to assign this IP address. """Check if the 'infra' right is required to assign this IP address."""
"""
return self.ip_type.need_infra return self.ip_type.need_infra
def clean(self): def clean(self):
@ -2223,20 +2180,13 @@ class IpList(RevMixin, AclMixin, models.Model):
""" """
can, _message, _group = IpType.can_use_all(user_request) can, _message, _group = IpType.can_use_all(user_request)
if can: if can:
return ( return (True, None, None, cls.objects.all())
True,
None,
None,
cls.objects.all()
)
else: else:
return ( return (
True, True,
_("You don't have the right to use all machine types."), _("You don't have the right to use all machine types."),
("machines.use_all_machinetype",), ("machines.use_all_machinetype",),
cls.objects.filter( cls.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False)),
ip_type__in=IpType.objects.filter(need_infra=False)
),
) )
def __str__(self): def __str__(self):
@ -2275,7 +2225,6 @@ class Role(RevMixin, AclMixin, models.Model):
specific_role = models.CharField(choices=ROLE, null=True, blank=True, max_length=32) specific_role = models.CharField(choices=ROLE, null=True, blank=True, max_length=32)
class Meta: class Meta:
permissions = (("view_role", _("Can view a role object")),)
verbose_name = _("server role") verbose_name = _("server role")
verbose_name_plural = _("server roles") verbose_name_plural = _("server roles")
@ -2325,7 +2274,6 @@ class Service(RevMixin, AclMixin, models.Model):
servers = models.ManyToManyField("Interface", through="Service_link") servers = models.ManyToManyField("Interface", through="Service_link")
class Meta: class Meta:
permissions = (("view_service", _("Can view a service object")),)
verbose_name = _("service to generate (DHCP, DNS, ...)") verbose_name = _("service to generate (DHCP, DNS, ...)")
verbose_name_plural = _("services to generate (DHCP, DNS, ...)") verbose_name_plural = _("services to generate (DHCP, DNS, ...)")
@ -2383,9 +2331,6 @@ class Service_link(RevMixin, AclMixin, models.Model):
asked_regen = models.BooleanField(default=False) asked_regen = models.BooleanField(default=False)
class Meta: class Meta:
permissions = (
("view_service_link", _("Can view a service server link object")),
)
verbose_name = _("link between service and server") verbose_name = _("link between service and server")
verbose_name_plural = _("links between service and server") verbose_name_plural = _("links between service and server")
@ -2438,9 +2383,6 @@ class OuverturePortList(RevMixin, AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = (
("view_ouvertureportlist", _("Can view a ports opening list" " object")),
)
verbose_name = _("ports opening list") verbose_name = _("ports opening list")
verbose_name_plural = _("ports opening lists") verbose_name_plural = _("ports opening lists")
@ -2542,7 +2484,13 @@ class OuverturePort(RevMixin, AclMixin, models.Model):
def machine_post_save(**kwargs): def machine_post_save(**kwargs):
"""Synchronise LDAP and regen firewall/DHCP after a machine is edited.""" """Synchronise LDAP and regen firewall/DHCP after a machine is edited."""
user = kwargs["instance"].user user = kwargs["instance"].user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True) users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=False,
mac_refresh=True,
)
regen("dhcp") regen("dhcp")
regen("mac_ip_list") regen("mac_ip_list")
@ -2552,7 +2500,13 @@ def machine_post_delete(**kwargs):
"""Synchronise LDAP and regen firewall/DHCP after a machine is deleted.""" """Synchronise LDAP and regen firewall/DHCP after a machine is deleted."""
machine = kwargs["instance"] machine = kwargs["instance"]
user = machine.user user = machine.user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True) users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=False,
mac_refresh=True,
)
regen("dhcp") regen("dhcp")
regen("mac_ip_list") regen("mac_ip_list")
@ -2565,7 +2519,13 @@ def interface_post_save(**kwargs):
interface = kwargs["instance"] interface = kwargs["instance"]
interface.sync_ipv6() interface.sync_ipv6()
user = interface.machine.user user = interface.machine.user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True) users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=False,
mac_refresh=True,
)
# Regen services # Regen services
regen("dhcp") regen("dhcp")
regen("mac_ip_list") regen("mac_ip_list")
@ -2577,11 +2537,16 @@ def interface_post_save(**kwargs):
@receiver(post_delete, sender=Interface) @receiver(post_delete, sender=Interface)
def interface_post_delete(**kwargs): def interface_post_delete(**kwargs):
"""Synchronise LDAP and regen firewall/DHCP after an interface is deleted. """Synchronise LDAP and regen firewall/DHCP after an interface is deleted."""
"""
interface = kwargs["instance"] interface = kwargs["instance"]
user = interface.machine.user user = interface.machine.user
users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True) users.signals.synchronise.send(
sender=users.models.User,
instance=user,
base=False,
access_refresh=False,
mac_refresh=True,
)
@receiver(post_save, sender=IpType) @receiver(post_save, sender=IpType)

View file

@ -26,142 +26,175 @@ The defined URLs for the Machines app
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url from django.urls import path
from . import views from . import views, views_autocomplete
from . import views_autocomplete
app_name = "machines"
urlpatterns = [ urlpatterns = [
url(r"^new_machine/(?P<userid>[0-9]+)$", views.new_machine, name="new-machine"), path("new_machine/<int:userid>", views.new_machine, name="new-machine"),
url( path(
r"^edit_interface/(?P<interfaceid>[0-9]+)$", "edit_interface/<int:interfaceid>",
views.edit_interface, views.edit_interface,
name="edit-interface", name="edit-interface",
), ),
url(r"^del_machine/(?P<machineid>[0-9]+)$", views.del_machine, name="del-machine"), path("del_machine/<int:machineid>", views.del_machine, name="del-machine"),
url( path(
r"^new_interface/(?P<machineid>[0-9]+)$", "new_interface/<int:machineid>",
views.new_interface, views.new_interface,
name="new-interface", name="new-interface",
), ),
url( path(
r"^del_interface/(?P<interfaceid>[0-9]+)$", "del_interface/<int:interfaceid>",
views.del_interface, views.del_interface,
name="del-interface", name="del-interface",
), ),
url(r"^add_machinetype/$", views.add_machinetype, name="add-machinetype"), path("add_machinetype", views.add_machinetype, name="add-machinetype"),
url( path(
r"^edit_machinetype/(?P<machinetypeid>[0-9]+)$", "edit_machinetype/<int:machinetypeid>",
views.edit_machinetype, views.edit_machinetype,
name="edit-machinetype", name="edit-machinetype",
), ),
url(r"^del_machinetype/$", views.del_machinetype, name="del-machinetype"), path("del_machinetype", views.del_machinetype, name="del-machinetype"),
url(r"^index_machinetype/$", views.index_machinetype, name="index-machinetype"), path("index_machinetype", views.index_machinetype, name="index-machinetype"),
url(r"^add_iptype/$", views.add_iptype, name="add-iptype"), path("add_iptype", views.add_iptype, name="add-iptype"),
url(r"^edit_iptype/(?P<iptypeid>[0-9]+)$", views.edit_iptype, name="edit-iptype"), path("edit_iptype/<int:iptypeid>", views.edit_iptype, name="edit-iptype"),
url(r"^del_iptype/$", views.del_iptype, name="del-iptype"), path("del_iptype", views.del_iptype, name="del-iptype"),
url(r"^index_iptype/$", views.index_iptype, name="index-iptype"), path("index_iptype", views.index_iptype, name="index-iptype"),
url(r"^add_extension/$", views.add_extension, name="add-extension"), path("add_extension", views.add_extension, name="add-extension"),
url( path(
r"^edit_extension/(?P<extensionid>[0-9]+)$", "edit_extension/<int:extensionid>",
views.edit_extension, views.edit_extension,
name="edit-extension", name="edit-extension",
), ),
url(r"^del_extension/$", views.del_extension, name="del-extension"), path("del_extension", views.del_extension, name="del-extension"),
url(r"^add_soa/$", views.add_soa, name="add-soa"), path("add_soa", views.add_soa, name="add-soa"),
url(r"^edit_soa/(?P<soaid>[0-9]+)$", views.edit_soa, name="edit-soa"), path("edit_soa/<int:soaid>", views.edit_soa, name="edit-soa"),
url(r"^del_soa/$", views.del_soa, name="del-soa"), path("del_soa", views.del_soa, name="del-soa"),
url(r"^add_mx/$", views.add_mx, name="add-mx"), path("add_mx", views.add_mx, name="add-mx"),
url(r"^edit_mx/(?P<mxid>[0-9]+)$", views.edit_mx, name="edit-mx"), path("edit_mx/<int:mxid>", views.edit_mx, name="edit-mx"),
url(r"^del_mx/$", views.del_mx, name="del-mx"), path("del_mx", views.del_mx, name="del-mx"),
url(r"^add_txt/$", views.add_txt, name="add-txt"), path("add_txt", views.add_txt, name="add-txt"),
url(r"^edit_txt/(?P<txtid>[0-9]+)$", views.edit_txt, name="edit-txt"), path("edit_txt/<int:txtid>", views.edit_txt, name="edit-txt"),
url(r"^del_txt/$", views.del_txt, name="del-txt"), path("del_txt", views.del_txt, name="del-txt"),
url(r"^add_dname/$", views.add_dname, name="add-dname"), path("add_dname", views.add_dname, name="add-dname"),
url(r"^edit_dname/(?P<dnameid>[0-9]+)$", views.edit_dname, name="edit-dname"), path("edit_dname/<int:dnameid>", views.edit_dname, name="edit-dname"),
url(r"^del_dname/$", views.del_dname, name="del-dname"), path("del_dname", views.del_dname, name="del-dname"),
url(r"^add_ns/$", views.add_ns, name="add-ns"), path("add_ns", views.add_ns, name="add-ns"),
url(r"^edit_ns/(?P<nsid>[0-9]+)$", views.edit_ns, name="edit-ns"), path("edit_ns/<int:nsid>", views.edit_ns, name="edit-ns"),
url(r"^del_ns/$", views.del_ns, name="del-ns"), path("del_ns", views.del_ns, name="del-ns"),
url(r"^add_srv/$", views.add_srv, name="add-srv"), path("add_srv", views.add_srv, name="add-srv"),
url(r"^edit_srv/(?P<srvid>[0-9]+)$", views.edit_srv, name="edit-srv"), path("edit_srv/<int:srvid>", views.edit_srv, name="edit-srv"),
url(r"^del_srv/$", views.del_srv, name="del-srv"), path("del_srv", views.del_srv, name="del-srv"),
url(r"^new_sshfp/(?P<machineid>[0-9]+)$", views.new_sshfp, name="new-sshfp"), path("new_sshfp/<int:machineid>", views.new_sshfp, name="new-sshfp"),
url(r"^edit_sshfp/(?P<sshfpid>[0-9]+)$", views.edit_sshfp, name="edit-sshfp"), path("edit_sshfp/<int:sshfpid>", views.edit_sshfp, name="edit-sshfp"),
url(r"^del_sshfp/(?P<sshfpid>[0-9]+)$", views.del_sshfp, name="del-sshfp"), path("del_sshfp/<int:sshfpid>", views.del_sshfp, name="del-sshfp"),
url(r"^index_sshfp/(?P<machineid>[0-9]+)$", views.index_sshfp, name="index-sshfp"), path("index_sshfp/<int:machineid>", views.index_sshfp, name="index-sshfp"),
url(r"^index_extension/$", views.index_extension, name="index-extension"), path("index_extension", views.index_extension, name="index-extension"),
url(r"^add_alias/(?P<interfaceid>[0-9]+)$", views.add_alias, name="add-alias"), path("add_alias/<int:interfaceid>", views.add_alias, name="add-alias"),
url(r"^edit_alias/(?P<domainid>[0-9]+)$", views.edit_alias, name="edit-alias"), path("edit_alias/<int:domainid>", views.edit_alias, name="edit-alias"),
url(r"^del_alias/(?P<interfaceid>[0-9]+)$", views.del_alias, name="del-alias"), path("del_alias/<int:interfaceid>", views.del_alias, name="del-alias"),
url( path("index_alias/<int:interfaceid>", views.index_alias, name="index-alias"),
r"^index_alias/(?P<interfaceid>[0-9]+)$", views.index_alias, name="index-alias" path(
), "new_ipv6list/<int:interfaceid>",
url(
r"^new_ipv6list/(?P<interfaceid>[0-9]+)$",
views.new_ipv6list, views.new_ipv6list,
name="new-ipv6list", name="new-ipv6list",
), ),
url( path(
r"^edit_ipv6list/(?P<ipv6listid>[0-9]+)$", "edit_ipv6list/<int:ipv6listid>",
views.edit_ipv6list, views.edit_ipv6list,
name="edit-ipv6list", name="edit-ipv6list",
), ),
url( path(
r"^del_ipv6list/(?P<ipv6listid>[0-9]+)$", "del_ipv6list/<int:ipv6listid>",
views.del_ipv6list, views.del_ipv6list,
name="del-ipv6list", name="del-ipv6list",
), ),
url(r"^index_ipv6/(?P<interfaceid>[0-9]+)$", views.index_ipv6, name="index-ipv6"), path("index_ipv6/<int:interfaceid>", views.index_ipv6, name="index-ipv6"),
url(r"^add_service/$", views.add_service, name="add-service"), path("add_service", views.add_service, name="add-service"),
url( path("edit_service/<int:serviceid>", views.edit_service, name="edit-service"),
r"^edit_service/(?P<serviceid>[0-9]+)$", views.edit_service, name="edit-service" path("del_service", views.del_service, name="del-service"),
), path(
url(r"^del_service/$", views.del_service, name="del-service"), "regen_service/<int:serviceid>",
url(
r"^regen_service/(?P<serviceid>[0-9]+)$",
views.regen_service, views.regen_service,
name="regen-service", name="regen-service",
), ),
url(r"^index_service/$", views.index_service, name="index-service"), path("index_service", views.index_service, name="index-service"),
url(r"^add_role/$", views.add_role, name="add-role"), path("add_role", views.add_role, name="add-role"),
url(r"^edit_role/(?P<roleid>[0-9]+)$", views.edit_role, name="edit-role"), path("edit_role/<int:roleid>", views.edit_role, name="edit-role"),
url(r"^del_role/$", views.del_role, name="del-role"), path("del_role", views.del_role, name="del-role"),
url(r"^index_role/$", views.index_role, name="index-role"), path("index_role", views.index_role, name="index-role"),
url(r"^add_vlan/$", views.add_vlan, name="add-vlan"), path("add_vlan", views.add_vlan, name="add-vlan"),
url(r"^edit_vlan/(?P<vlanid>[0-9]+)$", views.edit_vlan, name="edit-vlan"), path("edit_vlan/<int:vlanid>", views.edit_vlan, name="edit-vlan"),
url(r"^del_vlan/$", views.del_vlan, name="del-vlan"), path("del_vlan", views.del_vlan, name="del-vlan"),
url(r"^index_vlan/$", views.index_vlan, name="index-vlan"), path("index_vlan", views.index_vlan, name="index-vlan"),
url(r"^add_nas/$", views.add_nas, name="add-nas"), path("add_nas", views.add_nas, name="add-nas"),
url(r"^edit_nas/(?P<nasid>[0-9]+)$", views.edit_nas, name="edit-nas"), path("edit_nas/<int:nasid>", views.edit_nas, name="edit-nas"),
url(r"^del_nas/$", views.del_nas, name="del-nas"), path("del_nas", views.del_nas, name="del-nas"),
url(r"^index_nas/$", views.index_nas, name="index-nas"), path("index_nas", views.index_nas, name="index-nas"),
url(r"^$", views.index, name="index"), path("", views.index, name="index"),
url(r"index_portlist/$", views.index_portlist, name="index-portlist"), path("index_portlist", views.index_portlist, name="index-portlist"),
url( path(
r"^edit_portlist/(?P<ouvertureportlistid>[0-9]+)$", "edit_portlist/<int:ouvertureportlistid>",
views.edit_portlist, views.edit_portlist,
name="edit-portlist", name="edit-portlist",
), ),
url( path(
r"^del_portlist/(?P<ouvertureportlistid>[0-9]+)$", "del_portlist/<int:ouvertureportlistid>",
views.del_portlist, views.del_portlist,
name="del-portlist", name="del-portlist",
), ),
url(r"^add_portlist/$", views.add_portlist, name="add-portlist"), path("add_portlist", views.add_portlist, name="add-portlist"),
url( path(
r"^port_config/(?P<interfaceid>[0-9]+)$", "port_config/<int:interfaceid>",
views.configure_ports, views.configure_ports,
name="port-config", name="port-config",
), ),
### Autocomplete Views ### Autocomplete Views
url(r'^vlan-autocomplete/$', views_autocomplete.VlanAutocomplete.as_view(), name='vlan-autocomplete',), path(
url(r'^interface-autocomplete/$', views_autocomplete.InterfaceAutocomplete.as_view(), name='interface-autocomplete',), "vlan-autocomplete",
url(r'^machine-autocomplete/$', views_autocomplete.MachineAutocomplete.as_view(), name='machine-autocomplete',), views_autocomplete.VlanAutocomplete.as_view(),
url(r'^machinetype-autocomplete/$', views_autocomplete.MachineTypeAutocomplete.as_view(), name='machinetype-autocomplete',), name="vlan-autocomplete",
url(r'^iptype-autocomplete/$', views_autocomplete.IpTypeAutocomplete.as_view(), name='iptype-autocomplete',), ),
url(r'^extension-autocomplete/$', views_autocomplete.ExtensionAutocomplete.as_view(), name='extension-autocomplete',), path(
url(r'^domain-autocomplete/$', views_autocomplete.DomainAutocomplete.as_view(), name='domain-autocomplete',), "interface-autocomplete",
url(r'^ouvertureportlist-autocomplete/$', views_autocomplete.OuverturePortListAutocomplete.as_view(), name='ouvertureportlist-autocomplete',), views_autocomplete.InterfaceAutocomplete.as_view(),
url(r'^iplist-autocomplete/$', views_autocomplete.IpListAutocomplete.as_view(), name='iplist-autocomplete',), name="interface-autocomplete",
),
path(
"machine-autocomplete",
views_autocomplete.MachineAutocomplete.as_view(),
name="machine-autocomplete",
),
path(
"machinetype-autocomplete",
views_autocomplete.MachineTypeAutocomplete.as_view(),
name="machinetype-autocomplete",
),
path(
"iptype-autocomplete",
views_autocomplete.IpTypeAutocomplete.as_view(),
name="iptype-autocomplete",
),
path(
"extension-autocomplete",
views_autocomplete.ExtensionAutocomplete.as_view(),
name="extension-autocomplete",
),
path(
"domain-autocomplete",
views_autocomplete.DomainAutocomplete.as_view(),
name="domain-autocomplete",
),
path(
"ouvertureportlist-autocomplete",
views_autocomplete.OuverturePortListAutocomplete.as_view(),
name="ouvertureportlist-autocomplete",
),
path(
"iplist-autocomplete",
views_autocomplete.IpListAutocomplete.as_view(),
name="iplist-autocomplete",
),
] ]

View file

@ -34,11 +34,11 @@ from __future__ import unicode_literals
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required, permission_required
from django.db.models import ProtectedError, F
from django.db import IntegrityError from django.db import IntegrityError
from django.db.models import F, ProtectedError
from django.forms import modelformset_factory from django.forms import modelformset_factory
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render, redirect from django.shortcuts import redirect, render
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -46,82 +46,28 @@ from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
from preferences.models import GeneralOption from preferences.models import GeneralOption
from re2o.acl import ( from re2o.acl import (can_create, can_delete, can_delete_set, can_edit,
can_create, can_view, can_view_all)
can_edit,
can_view,
can_delete,
can_view_all,
can_delete_set,
)
from re2o.utils import all_active_assigned_interfaces, filter_active_interfaces
from re2o.base import SortTable, re2o_paginator from re2o.base import SortTable, re2o_paginator
from re2o.utils import all_active_assigned_interfaces, filter_active_interfaces
from re2o.views import form from re2o.views import form
from users.models import User from users.models import User
from .forms import (
NewMachineForm, from .forms import (AddInterfaceForm, AliasForm, DelAliasForm, DelDNameForm,
EditMachineForm, DelExtensionForm, DelIpTypeForm, DelMachineTypeForm,
EditInterfaceForm, DelMxForm, DelNasForm, DelNsForm, DelRoleForm,
AddInterfaceForm, DelServiceForm, DelSOAForm, DelSrvForm, DelTxtForm,
MachineTypeForm, DelVlanForm, DNameForm, DomainForm, EditInterfaceForm,
DelMachineTypeForm, EditIpTypeForm, EditMachineForm,
ExtensionForm, EditOuverturePortConfigForm, EditOuverturePortListForm,
DelExtensionForm, ExtensionForm, IpTypeForm, Ipv6ListForm, MachineTypeForm,
EditIpTypeForm, MxForm, NasForm, NewMachineForm, NsForm, RoleForm,
IpTypeForm, ServiceForm, SOAForm, SrvForm, SshFpForm, TxtForm,
DelIpTypeForm, VlanForm)
DomainForm, from .models import (SOA, DName, Domain, Extension, Interface, IpType,
AliasForm, Ipv6List, Machine, MachineType, Mx, Nas, Ns,
DelAliasForm, OuverturePort, OuverturePortList, Role, Service,
SOAForm, Service_link, Srv, SshFp, Txt, Vlan, regen)
DelSOAForm,
NsForm,
DelNsForm,
TxtForm,
DelTxtForm,
DNameForm,
DelDNameForm,
MxForm,
DelMxForm,
VlanForm,
DelVlanForm,
RoleForm,
DelRoleForm,
ServiceForm,
DelServiceForm,
SshFpForm,
NasForm,
DelNasForm,
SrvForm,
DelSrvForm,
Ipv6ListForm,
EditOuverturePortListForm,
EditOuverturePortConfigForm,
)
from .models import (
IpType,
Machine,
Interface,
MachineType,
Extension,
SOA,
Mx,
Ns,
Domain,
Role,
Service,
Service_link,
regen,
Vlan,
Nas,
Txt,
DName,
Srv,
SshFp,
OuverturePortList,
OuverturePort,
Ipv6List,
)
@login_required @login_required
@ -135,7 +81,9 @@ def new_machine(request, user, **_kwargs):
""" """
machine = NewMachineForm(request.POST or None, user=request.user) machine = NewMachineForm(request.POST or None, user=request.user)
interface = AddInterfaceForm(request.POST or None, user=request.user) interface = AddInterfaceForm(request.POST or None, user=request.user)
domain = DomainForm(request.POST or None, user=user, initial={'name': user.get_next_domain_name()}) domain = DomainForm(
request.POST or None, user=user, initial={"name": user.get_next_domain_name()}
)
if machine.is_valid() and interface.is_valid(): if machine.is_valid() and interface.is_valid():
new_machine_obj = machine.save(commit=False) new_machine_obj = machine.save(commit=False)
new_machine_obj.user = user new_machine_obj.user = user
@ -229,7 +177,11 @@ def new_interface(request, machine, **_kwargs):
machine. machine.
""" """
interface_form = AddInterfaceForm(request.POST or None, user=request.user) interface_form = AddInterfaceForm(request.POST or None, user=request.user)
domain_form = DomainForm(request.POST or None, user=request.user, initial={'name': machine.user.get_next_domain_name()}) domain_form = DomainForm(
request.POST or None,
user=request.user,
initial={"name": machine.user.get_next_domain_name()},
)
if interface_form.is_valid(): if interface_form.is_valid():
new_interface_obj = interface_form.save(commit=False) new_interface_obj = interface_form.save(commit=False)
domain_form.instance.interface_parent = new_interface_obj domain_form.instance.interface_parent = new_interface_obj
@ -268,7 +220,9 @@ def del_interface(request, interface, **_kwargs):
reverse("users:profil", kwargs={"userid": str(request.user.id)}) reverse("users:profil", kwargs={"userid": str(request.user.id)})
) )
return form( return form(
{"objet": interface, "objet_name": _("interface")}, "machines/delete.html", request {"objet": interface, "objet_name": _("interface")},
"machines/delete.html",
request,
) )
@ -328,7 +282,9 @@ def del_ipv6list(request, ipv6list, **_kwargs):
reverse("machines:index-ipv6", kwargs={"interfaceid": str(interfaceid)}) reverse("machines:index-ipv6", kwargs={"interfaceid": str(interfaceid)})
) )
return form( return form(
{"objet": ipv6list, "objet_name": _("IPv6 addresses list")}, "machines/delete.html", request {"objet": ipv6list, "objet_name": _("IPv6 addresses list")},
"machines/delete.html",
request,
) )
@ -384,7 +340,9 @@ def del_sshfp(request, sshfp, **_kwargs):
reverse("machines:index-sshfp", kwargs={"machineid": str(machineid)}) reverse("machines:index-sshfp", kwargs={"machineid": str(machineid)})
) )
return form( return form(
{"objet": sshfp, "objet_name": _("SSHFP record")}, "machines/delete.html", request {"objet": sshfp, "objet_name": _("SSHFP record")},
"machines/delete.html",
request,
) )
@ -421,7 +379,9 @@ def edit_iptype(request, iptype_instance, **_kwargs):
iptype.save() iptype.save()
messages.success(request, _("The IP type was edited.")) messages.success(request, _("The IP type was edited."))
except IntegrityError as e: except IntegrityError as e:
messages.success(request, _("This IP type change would create duplicated domains")) messages.success(
request, _("This IP type change would create duplicated domains")
)
return redirect(reverse("machines:index-iptype")) return redirect(reverse("machines:index-iptype"))
return form( return form(
{"iptypeform": iptype, "action_name": _("Edit")}, {"iptypeform": iptype, "action_name": _("Edit")},
@ -490,7 +450,10 @@ def edit_machinetype(request, machinetype_instance, **_kwargs):
machinetype.save() machinetype.save()
messages.success(request, _("The machine type was edited.")) messages.success(request, _("The machine type was edited."))
except IntegrityError as e: except IntegrityError as e:
messages.error(request, _("This machine type change would create duplicated domains")) messages.error(
request,
_("This machine type change would create duplicated domains"),
)
return redirect(reverse("machines:index-machinetype")) return redirect(reverse("machines:index-machinetype"))
return form( return form(
{"machinetypeform": machinetype, "action_name": _("Edit")}, {"machinetypeform": machinetype, "action_name": _("Edit")},
@ -580,10 +543,16 @@ def del_extension(request, instances):
_( _(
"The extension %s is assigned to following %s : %s" "The extension %s is assigned to following %s : %s"
", you can't delete it." ", you can't delete it."
) % ( )
% (
extension_del, extension_del,
str(e.protected_objects.model._meta.verbose_name_plural), str(e.protected_objects.model._meta.verbose_name_plural),
",".join(map(lambda x: str(x['name']), e.protected_objects.values('name').iterator())) ",".join(
map(
lambda x: str(x["name"]),
e.protected_objects.values("name").iterator(),
)
),
) )
), ),
) )
@ -1221,11 +1190,12 @@ def index(request):
machines_list = re2o_paginator(request, machines_list, pagination_large_number) machines_list = re2o_paginator(request, machines_list, pagination_large_number)
return render(request, "machines/index.html", {"machines_list": machines_list}) return render(request, "machines/index.html", {"machines_list": machines_list})
# Canonic view for displaying machines in users's profil # Canonic view for displaying machines in users's profil
def aff_profil(request, user): def aff_profil(request, user):
"""View used to display the machines on a user's profile.""" """View used to display the machines on a user's profile."""
machines = ( machines = (
Machine.objects.filter(user=user) Machine.objects.filter(user=user)
.select_related("user") .select_related("user")
.prefetch_related("interface_set__domain__extension") .prefetch_related("interface_set__domain__extension")
.prefetch_related("interface_set__ipv4__ip_type__extension") .prefetch_related("interface_set__ipv4__ip_type__extension")
@ -1243,19 +1213,16 @@ def aff_profil(request, user):
machines = re2o_paginator(request, machines, pagination_large_number) machines = re2o_paginator(request, machines, pagination_large_number)
context = { context = {
"users":user, "users": user,
"machines_list": machines, "machines_list": machines,
"nb_machines":nb_machines, "nb_machines": nb_machines,
} }
return render_to_string( return render_to_string(
"machines/aff_profil.html",context=context,request=request,using=None "machines/aff_profil.html", context=context, request=request, using=None
) )
@login_required @login_required
@can_view_all(IpType) @can_view_all(IpType)
def index_iptype(request): def index_iptype(request):
@ -1531,4 +1498,3 @@ def configure_ports(request, interface_instance, **_kwargs):
"machines/machine.html", "machines/machine.html",
request, request,
) )

View file

@ -31,23 +31,14 @@ Here are defined the autocomplete class based view.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db.models import Q, Value, CharField from django.db.models import CharField, Q, Value
from django.db.models.functions import Concat from django.db.models.functions import Concat
from .models import (
Interface,
Machine,
Vlan,
MachineType,
IpType,
Extension,
Domain,
OuverturePortList,
IpList,
)
from re2o.views import AutocompleteViewMixin from re2o.views import AutocompleteViewMixin
from .models import (Domain, Extension, Interface, IpList, IpType, Machine,
MachineType, OuverturePortList, Vlan)
class VlanAutocomplete(AutocompleteViewMixin): class VlanAutocomplete(AutocompleteViewMixin):
obj_type = Vlan obj_type = Vlan

View file

@ -27,11 +27,11 @@ Select a dorm
from django import forms from django import forms
from django.forms import ModelForm, Form from django.forms import Form, ModelForm
from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin
from topologie.models import Dormitory from topologie.models import Dormitory
from .preferences.models import MultiopOption from .preferences.models import MultiopOption
@ -49,4 +49,6 @@ class DormitoryForm(FormRevMixin, Form):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(DormitoryForm, self).__init__(*args, **kwargs) super(DormitoryForm, self).__init__(*args, **kwargs)
self.fields["dormitory"].queryset = MultiopOption.get_cached_value("enabled_dorm").all() self.fields["dormitory"].queryset = MultiopOption.get_cached_value(
"enabled_dorm"
).all()

View file

@ -25,21 +25,18 @@ Multi_op model
from __future__ import absolute_import from __future__ import absolute_import
from django.core.mail import EmailMessage
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.template import loader
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.template import loader
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from reversion.models import Version from reversion.models import Version
from re2o.mixins import AclMixin
from re2o.mail_utils import send_mail_object
from django.core.mail import EmailMessage
from preferences.models import GeneralOption
import users.models import users.models
from preferences.models import GeneralOption
from re2o.mail_utils import send_mail_object
from re2o.mixins import AclMixin
from .preferences.models import MultiopOption from .preferences.models import MultiopOption

View file

@ -27,8 +27,9 @@ each.
from django import forms from django import forms
from django.forms import ModelForm, Form from django.forms import Form, ModelForm
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from re2o.widgets import AutocompleteMultipleModelWidget from re2o.widgets import AutocompleteMultipleModelWidget
from .models import MultiopOption from .models import MultiopOption

View file

@ -27,8 +27,8 @@ with multiple operators.
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from re2o.mixins import AclMixin, RevMixin
from preferences.models import PreferencesModel from preferences.models import PreferencesModel
from re2o.mixins import AclMixin, RevMixin
class MultiopOption(AclMixin, PreferencesModel): class MultiopOption(AclMixin, PreferencesModel):

View file

@ -26,20 +26,16 @@
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect from django.shortcuts import redirect, render
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext as _
from re2o.base import re2o_paginator
from re2o.acl import can_view, can_view_all, can_edit, can_create
from preferences.views import edit_options_template_function from preferences.views import edit_options_template_function
from re2o.acl import can_create, can_edit, can_view, can_view_all
from re2o.base import re2o_paginator
from . import forms from . import forms, models
from . import models
def aff_preferences(request): def aff_preferences(request):

View file

@ -25,35 +25,37 @@ For further details on each of those models, see the documentation details for
each. each.
""" """
from django.conf.urls import url from django.urls import path, re_path
from . import views from . import views
from .preferences.views import edit_options from .preferences.views import edit_options
app_name = "multi_op"
urlpatterns = [ urlpatterns = [
url(r"^$", views.aff_state_global, name="aff-state-global"), path("", views.aff_state_global, name="aff-state-global"),
url( path(
r"^(?P<dormitoryid>[0-9]+)$", "<int:dormitoryid>",
views.aff_state_dormitory, views.aff_state_dormitory,
name="aff-state-dormitory", name="aff-state-dormitory",
), ),
url( re_path(
r"^edit_options/(?P<section>MultiopOption)$", r"^edit_options/(?P<section>MultiopOption)$",
edit_options, edit_options,
name="edit-options", name="edit-options",
), ),
url( path(
r"^pending-connection$", "pending-connection",
views.aff_pending_connection, views.aff_pending_connection,
name="aff-pending-connection", name="aff-pending-connection",
), ),
url( path(
r"^pending-disconnection$", "pending-disconnection",
views.aff_pending_disconnection, views.aff_pending_disconnection,
name="aff-pending-disconnection", name="aff-pending-disconnection",
), ),
url( path(
r"^disconnect-room/(?P<roomid>[0-9]+)$", "disconnect-room/<int:roomid>",
views.disconnect_room, views.disconnect_room,
name="disconnect-room", name="disconnect-room",
), ),

View file

@ -27,28 +27,24 @@
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.template.loader import render_to_string
from django.views.decorators.cache import cache_page
from django.utils.translation import ugettext as _
from django.urls import reverse
from django.forms import modelformset_factory
from django.db.models import Q from django.db.models import Q
from django.forms import modelformset_factory
from django.shortcuts import redirect, render
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.translation import ugettext as _
from django.views.decorators.cache import cache_page
from preferences.models import AssoOption, GeneralOption
from re2o.acl import can_create, can_edit, can_view, can_view_all
from re2o.base import SortTable, re2o_paginator
from re2o.utils import all_adherent, all_has_access
from re2o.views import form from re2o.views import form
from re2o.utils import all_has_access, all_adherent from topologie.models import Dormitory, Room
from re2o.base import re2o_paginator, SortTable
from re2o.acl import can_view, can_view_all, can_edit, can_create
from preferences.models import GeneralOption, AssoOption
from .forms import DormitoryForm from .forms import DormitoryForm
from .preferences.models import MultiopOption from .preferences.models import MultiopOption
from topologie.models import Room, Dormitory
def display_rooms_connection(request, dormitory=None): def display_rooms_connection(request, dormitory=None):
"""View used to display an overview of the rooms' connection state. """View used to display an overview of the rooms' connection state.
@ -58,9 +54,13 @@ def display_rooms_connection(request, dormitory=None):
dormitory: Dormitory, the dormitory used to filter rooms. If no dormitory: Dormitory, the dormitory used to filter rooms. If no
dormitory is given, all rooms are displayed (default: None). dormitory is given, all rooms are displayed (default: None).
""" """
room_list = Room.objects.select_related("building__dormitory").filter( room_list = (
building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all() Room.objects.select_related("building__dormitory")
).order_by("building_dormitory", "port") .filter(
building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all()
)
.order_by("building_dormitory", "port")
)
if dormitory: if dormitory:
room_list = room_list.filter(building__dormitory=dormitory) room_list = room_list.filter(building__dormitory=dormitory)
room_list = SortTable.sort( room_list = SortTable.sort(
@ -113,7 +113,9 @@ def aff_pending_connection(request):
Room.objects.select_related("building__dormitory") Room.objects.select_related("building__dormitory")
.filter(port__isnull=True) .filter(port__isnull=True)
.filter(adherent__in=all_has_access()) .filter(adherent__in=all_has_access())
.filter(building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all()) .filter(
building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all()
)
.order_by("building_dormitory", "port") .order_by("building_dormitory", "port")
) )
dormitory_form = DormitoryForm(request.POST or None) dormitory_form = DormitoryForm(request.POST or None)
@ -151,7 +153,9 @@ def aff_pending_disconnection(request):
Room.objects.select_related("building__dormitory") Room.objects.select_related("building__dormitory")
.filter(port__isnull=False) .filter(port__isnull=False)
.exclude(Q(adherent__in=all_has_access()) | Q(adherent__in=all_adherent())) .exclude(Q(adherent__in=all_has_access()) | Q(adherent__in=all_adherent()))
.filter(building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all()) .filter(
building__dormitory__in=MultiopOption.get_cached_value("enabled_dorm").all()
)
.order_by("building_dormitory", "port") .order_by("building_dormitory", "port")
) )
dormitory_form = DormitoryForm(request.POST or None) dormitory_form = DormitoryForm(request.POST or None)

142
mysql-requirements.txt Normal file
View file

@ -0,0 +1,142 @@
django-autocomplete-light==3.8.1 \
--hash=sha256:4e84a6d95d272b0d7221614332e2bd54ffff15ec06e78947279398f6507ce225
django-bootstrap3==14.2.0; python_version >= "3.6" and python_version < "4.0" \
--hash=sha256:1fc3db37f29bcd159c0b00e1f15fd2d6dedf7551ce8bcca09072e33663c110de \
--hash=sha256:c7e6912b5127bea913bc8504399613758760954140162e048184624765e61904
django-ldapdb==1.5.1; python_version >= "3.6" \
--hash=sha256:5ea333c3130abbb86f8629766370a7f490878706469fce4e5008e41fb566392a \
--hash=sha256:2daee828a7eb1ce6ad0634ce5311339d77c08128829e575ff14d62a46771b86a
django-macaddress==1.7.0 \
--hash=sha256:342aa3421ee19acc3661d1705dad2ae674eaa7d06a02799f7a5dc394d6233275 \
--hash=sha256:db8beedcbd708aba3a6c9e83de6527efa2f9cc7dbe698406cebdfdbaf98c896d
django-rest-framework==0.1.0 \
--hash=sha256:47a8f496fa69e3b6bd79f68dd7a1527d907d6b77f009e9db7cf9bb21cc565e4a
django-reversion==3.0.9; python_version >= "3.6" \
--hash=sha256:a5af55f086a3f9c38be2f049c251e06005b9ed48ba7a109473736b1fc95a066f \
--hash=sha256:1b57127a136b969f4b843a915c72af271febe7f336469db6c27121f8adcad35c
django==2.2.18; python_version >= "3.5" \
--hash=sha256:0eaca08f236bf502a9773e53623f766cc3ceee6453cc41e6de1c8b80f07d2364 \
--hash=sha256:c9c994f5e0a032cbd45089798b52e4080f4dea7241c58e3e0636c54146480bb4
djangorestframework==3.12.2; python_version >= "3.5" \
--hash=sha256:0209bafcb7b5010fdfec784034f059d512256424de2a0f084cb82b096d6dd6a7 \
--hash=sha256:0898182b4737a7b584a2c73735d89816343369f259fea932d90dc78e35d8ac33
gitdb==4.0.5; python_version >= "3.4" \
--hash=sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac \
--hash=sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9
gitpython==3.1.13; python_version >= "3.4" \
--hash=sha256:c5347c81d232d9b8e7f47b68a83e5dc92e7952127133c5f2df9133f2c75a1b29 \
--hash=sha256:8621a7e777e276a5ec838b59280ba5272dd144a18169c36c903d8b38b99f750a
importlib-metadata==1.7.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.6" and python_version < "3.8" and python_full_version >= "3.5.0" \
--hash=sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070 \
--hash=sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83
importlib-resources==5.1.0; python_version >= "3.6" and python_version < "3.7" \
--hash=sha256:885b8eae589179f661c909d699a546cf10d83692553e34dca1bf5eb06f7f6217 \
--hash=sha256:bfdad047bce441405a49cf8eb48ddce5e56c696e185f59147a8b79e75e9e6380
mysqlclient==2.0.3; python_version >= "3.5" \
--hash=sha256:3381ca1a4f37ff1155fcfde20836b46416d66531add8843f6aa6d968982731c3 \
--hash=sha256:0ac0dd759c4ca02c35a9fedc24bc982cf75171651e8187c2495ec957a87dfff7 \
--hash=sha256:71c4b330cf2313bbda0307fc858cc9055e64493ba9bf28454d25cf8b3ee8d7f5 \
--hash=sha256:fc575093cf81b6605bed84653e48b277318b880dc9becf42dd47fa11ffd3e2b6 \
--hash=sha256:f6ebea7c008f155baeefe16c56cd3ee6239f7a5a9ae42396c2f1860f08a7c432
netaddr==0.8.0 \
--hash=sha256:9666d0232c32d2656e5e5f8d735f58fd6c7457ce52fc21c98d45f2af78f990ac \
--hash=sha256:d6cc57c7a07b1d9d2e917aa8b36ae8ce61c35ba3fcd1b83ca31c5a0ee2b5a243
pillow==8.1.0; python_version >= "3.6" \
--hash=sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a \
--hash=sha256:93a473b53cc6e0b3ce6bf51b1b95b7b1e7e6084be3a07e40f79b42e83503fbf2 \
--hash=sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174 \
--hash=sha256:1d208e670abfeb41b6143537a681299ef86e92d2a3dac299d3cd6830d5c7bded \
--hash=sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d \
--hash=sha256:b09e10ec453de97f9a23a5aa5e30b334195e8d2ddd1ce76cc32e52ba63c8b31d \
--hash=sha256:b02a0b9f332086657852b1f7cb380f6a42403a6d9c42a4c34a561aa4530d5234 \
--hash=sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8 \
--hash=sha256:604815c55fd92e735f9738f65dabf4edc3e79f88541c221d292faec1904a4b17 \
--hash=sha256:cf6e33d92b1526190a1de904df21663c46a456758c0424e4f947ae9aa6088bf7 \
--hash=sha256:47c0d93ee9c8b181f353dbead6530b26980fe4f5485aa18be8f1fd3c3cbc685e \
--hash=sha256:96d4dc103d1a0fa6d47c6c55a47de5f5dafd5ef0114fa10c85a1fd8e0216284b \
--hash=sha256:7916cbc94f1c6b1301ac04510d0881b9e9feb20ae34094d3615a8a7c3db0dcc0 \
--hash=sha256:3de6b2ee4f78c6b3d89d184ade5d8fa68af0848f9b6b6da2b9ab7943ec46971a \
--hash=sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d \
--hash=sha256:f50e7a98b0453f39000619d845be8b06e611e56ee6e8186f7f60c3b1e2f0feae \
--hash=sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59 \
--hash=sha256:6c5275bd82711cd3dcd0af8ce0bb99113ae8911fc2952805f1d012de7d600a4c \
--hash=sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6 \
--hash=sha256:5e2fe3bb2363b862671eba632537cd3a823847db4d98be95690b7e382f3d6378 \
--hash=sha256:7612520e5e1a371d77e1d1ca3a3ee6227eef00d0a9cddb4ef7ecb0b7396eddf7 \
--hash=sha256:d673c4990acd016229a5c1c4ee8a9e6d8f481b27ade5fc3d95938697fa443ce0 \
--hash=sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b \
--hash=sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865 \
--hash=sha256:a3d3e086474ef12ef13d42e5f9b7bbf09d39cf6bd4940f982263d6954b13f6a9 \
--hash=sha256:731ca5aabe9085160cf68b2dbef95fc1991015bc0a3a6ea46a371ab88f3d0913 \
--hash=sha256:bba80df38cfc17f490ec651c73bb37cd896bc2400cfba27d078c2135223c1206 \
--hash=sha256:c3d911614b008e8a576b8e5303e3db29224b455d3d66d1b2848ba6ca83f9ece9 \
--hash=sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032 \
--hash=sha256:81c3fa9a75d9f1afafdb916d5995633f319db09bd773cb56b8e39f1e98d90820 \
--hash=sha256:b6f00ad5ebe846cc91763b1d0c6d30a8042e02b2316e27b05de04fa6ec831ec5 \
--hash=sha256:887668e792b7edbfb1d3c9d8b5d8c859269a0f0eba4dda562adb95500f60dbba
psycopg2==2.8.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") \
--hash=sha256:068115e13c70dc5982dfc00c5d70437fe37c014c808acce119b5448361c03725 \
--hash=sha256:d160744652e81c80627a909a0e808f3c6653a40af435744de037e3172cf277f5 \
--hash=sha256:b8cae8b2f022efa1f011cc753adb9cbadfa5a184431d09b273fb49b4167561ad \
--hash=sha256:f22ea9b67aea4f4a1718300908a2fb62b3e4276cf00bd829a97ab5894af42ea3 \
--hash=sha256:26e7fd115a6db75267b325de0fba089b911a4a12ebd3d0b5e7acb7028bc46821 \
--hash=sha256:00195b5f6832dbf2876b8bf77f12bdce648224c89c880719c745b90515233301 \
--hash=sha256:a49833abfdede8985ba3f3ec641f771cca215479f41523e99dace96d5b8cce2a \
--hash=sha256:f974c96fca34ae9e4f49839ba6b78addf0346777b46c4da27a7bf54f48d3057d \
--hash=sha256:6a3d9efb6f36f1fe6aa8dbb5af55e067db802502c55a9defa47c5a1dad41df84 \
--hash=sha256:56fee7f818d032f802b8eed81ef0c1232b8b42390df189cab9cfa87573fe52c5 \
--hash=sha256:ad2fe8a37be669082e61fb001c185ffb58867fdbb3e7a6b0b0d2ffe232353a3e \
--hash=sha256:56007a226b8e95aa980ada7abdea6b40b75ce62a433bd27cec7a8178d57f4051 \
--hash=sha256:2c93d4d16933fea5bbacbe1aaf8fa8c1348740b2e50b3735d1b0bf8154cbf0f3 \
--hash=sha256:d5062ae50b222da28253059880a871dc87e099c25cb68acf613d9d227413d6f7 \
--hash=sha256:fb23f6c71107c37fd667cb4ea363ddeb936b348bbd6449278eb92c189699f543
pyasn1-modules==0.2.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e \
--hash=sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199 \
--hash=sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405 \
--hash=sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb \
--hash=sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8 \
--hash=sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74 \
--hash=sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d \
--hash=sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45 \
--hash=sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4 \
--hash=sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811 \
--hash=sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed \
--hash=sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0 \
--hash=sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd
pyasn1==0.4.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3 \
--hash=sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf \
--hash=sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00 \
--hash=sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8 \
--hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \
--hash=sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86 \
--hash=sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7 \
--hash=sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576 \
--hash=sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12 \
--hash=sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2 \
--hash=sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359 \
--hash=sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776 \
--hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba
pycrypto==2.6.1 \
--hash=sha256:f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c
python-dateutil==2.8.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") \
--hash=sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c \
--hash=sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a
python-ldap==3.3.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:4711cacf013e298754abd70058ccc995758177fb425f1c2d30e71adfc1d00aa5
pytz==2021.1; python_version >= "3.6" and python_version < "4.0" \
--hash=sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798 \
--hash=sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da
six==1.15.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" \
--hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced \
--hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259
smmap==3.0.5; python_version >= "3.4" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.4" \
--hash=sha256:7bfcf367828031dc893530a29cb35eb8c8f2d7c8f2d0989354d75d24c8573714 \
--hash=sha256:84c2751ef3072d4f6b2785ec7ee40244c6f45eb934d9e543e2c51f1bd3d54c50
sqlparse==0.4.1; python_version >= "3.6" and python_version < "4.0" \
--hash=sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0 \
--hash=sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8
zipp==3.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_version < "3.7" and python_full_version >= "3.5.0" \
--hash=sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108 \
--hash=sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb

1213
poetry.lock generated Normal file

File diff suppressed because it is too large Load diff

142
postgresql-requirements.txt Normal file
View file

@ -0,0 +1,142 @@
django-autocomplete-light==3.8.1 \
--hash=sha256:4e84a6d95d272b0d7221614332e2bd54ffff15ec06e78947279398f6507ce225
django-bootstrap3==14.2.0; python_version >= "3.6" and python_version < "4.0" \
--hash=sha256:1fc3db37f29bcd159c0b00e1f15fd2d6dedf7551ce8bcca09072e33663c110de \
--hash=sha256:c7e6912b5127bea913bc8504399613758760954140162e048184624765e61904
django-ldapdb==1.5.1; python_version >= "3.6" \
--hash=sha256:5ea333c3130abbb86f8629766370a7f490878706469fce4e5008e41fb566392a \
--hash=sha256:2daee828a7eb1ce6ad0634ce5311339d77c08128829e575ff14d62a46771b86a
django-macaddress==1.7.0 \
--hash=sha256:342aa3421ee19acc3661d1705dad2ae674eaa7d06a02799f7a5dc394d6233275 \
--hash=sha256:db8beedcbd708aba3a6c9e83de6527efa2f9cc7dbe698406cebdfdbaf98c896d
django-rest-framework==0.1.0 \
--hash=sha256:47a8f496fa69e3b6bd79f68dd7a1527d907d6b77f009e9db7cf9bb21cc565e4a
django-reversion==3.0.9; python_version >= "3.6" \
--hash=sha256:a5af55f086a3f9c38be2f049c251e06005b9ed48ba7a109473736b1fc95a066f \
--hash=sha256:1b57127a136b969f4b843a915c72af271febe7f336469db6c27121f8adcad35c
django==2.2.18; python_version >= "3.5" \
--hash=sha256:0eaca08f236bf502a9773e53623f766cc3ceee6453cc41e6de1c8b80f07d2364 \
--hash=sha256:c9c994f5e0a032cbd45089798b52e4080f4dea7241c58e3e0636c54146480bb4
djangorestframework==3.12.2; python_version >= "3.5" \
--hash=sha256:0209bafcb7b5010fdfec784034f059d512256424de2a0f084cb82b096d6dd6a7 \
--hash=sha256:0898182b4737a7b584a2c73735d89816343369f259fea932d90dc78e35d8ac33
gitdb==4.0.5; python_version >= "3.4" \
--hash=sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac \
--hash=sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9
gitpython==3.1.13; python_version >= "3.4" \
--hash=sha256:c5347c81d232d9b8e7f47b68a83e5dc92e7952127133c5f2df9133f2c75a1b29 \
--hash=sha256:8621a7e777e276a5ec838b59280ba5272dd144a18169c36c903d8b38b99f750a
importlib-metadata==1.7.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.6" and python_version < "3.8" and python_full_version >= "3.5.0" \
--hash=sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070 \
--hash=sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83
importlib-resources==5.1.0; python_version >= "3.6" and python_version < "3.7" \
--hash=sha256:885b8eae589179f661c909d699a546cf10d83692553e34dca1bf5eb06f7f6217 \
--hash=sha256:bfdad047bce441405a49cf8eb48ddce5e56c696e185f59147a8b79e75e9e6380
mysqlclient==2.0.3; python_version >= "3.5" \
--hash=sha256:3381ca1a4f37ff1155fcfde20836b46416d66531add8843f6aa6d968982731c3 \
--hash=sha256:0ac0dd759c4ca02c35a9fedc24bc982cf75171651e8187c2495ec957a87dfff7 \
--hash=sha256:71c4b330cf2313bbda0307fc858cc9055e64493ba9bf28454d25cf8b3ee8d7f5 \
--hash=sha256:fc575093cf81b6605bed84653e48b277318b880dc9becf42dd47fa11ffd3e2b6 \
--hash=sha256:f6ebea7c008f155baeefe16c56cd3ee6239f7a5a9ae42396c2f1860f08a7c432
netaddr==0.8.0 \
--hash=sha256:9666d0232c32d2656e5e5f8d735f58fd6c7457ce52fc21c98d45f2af78f990ac \
--hash=sha256:d6cc57c7a07b1d9d2e917aa8b36ae8ce61c35ba3fcd1b83ca31c5a0ee2b5a243
pillow==8.1.0; python_version >= "3.6" \
--hash=sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a \
--hash=sha256:93a473b53cc6e0b3ce6bf51b1b95b7b1e7e6084be3a07e40f79b42e83503fbf2 \
--hash=sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174 \
--hash=sha256:1d208e670abfeb41b6143537a681299ef86e92d2a3dac299d3cd6830d5c7bded \
--hash=sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d \
--hash=sha256:b09e10ec453de97f9a23a5aa5e30b334195e8d2ddd1ce76cc32e52ba63c8b31d \
--hash=sha256:b02a0b9f332086657852b1f7cb380f6a42403a6d9c42a4c34a561aa4530d5234 \
--hash=sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8 \
--hash=sha256:604815c55fd92e735f9738f65dabf4edc3e79f88541c221d292faec1904a4b17 \
--hash=sha256:cf6e33d92b1526190a1de904df21663c46a456758c0424e4f947ae9aa6088bf7 \
--hash=sha256:47c0d93ee9c8b181f353dbead6530b26980fe4f5485aa18be8f1fd3c3cbc685e \
--hash=sha256:96d4dc103d1a0fa6d47c6c55a47de5f5dafd5ef0114fa10c85a1fd8e0216284b \
--hash=sha256:7916cbc94f1c6b1301ac04510d0881b9e9feb20ae34094d3615a8a7c3db0dcc0 \
--hash=sha256:3de6b2ee4f78c6b3d89d184ade5d8fa68af0848f9b6b6da2b9ab7943ec46971a \
--hash=sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d \
--hash=sha256:f50e7a98b0453f39000619d845be8b06e611e56ee6e8186f7f60c3b1e2f0feae \
--hash=sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59 \
--hash=sha256:6c5275bd82711cd3dcd0af8ce0bb99113ae8911fc2952805f1d012de7d600a4c \
--hash=sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6 \
--hash=sha256:5e2fe3bb2363b862671eba632537cd3a823847db4d98be95690b7e382f3d6378 \
--hash=sha256:7612520e5e1a371d77e1d1ca3a3ee6227eef00d0a9cddb4ef7ecb0b7396eddf7 \
--hash=sha256:d673c4990acd016229a5c1c4ee8a9e6d8f481b27ade5fc3d95938697fa443ce0 \
--hash=sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b \
--hash=sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865 \
--hash=sha256:a3d3e086474ef12ef13d42e5f9b7bbf09d39cf6bd4940f982263d6954b13f6a9 \
--hash=sha256:731ca5aabe9085160cf68b2dbef95fc1991015bc0a3a6ea46a371ab88f3d0913 \
--hash=sha256:bba80df38cfc17f490ec651c73bb37cd896bc2400cfba27d078c2135223c1206 \
--hash=sha256:c3d911614b008e8a576b8e5303e3db29224b455d3d66d1b2848ba6ca83f9ece9 \
--hash=sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032 \
--hash=sha256:81c3fa9a75d9f1afafdb916d5995633f319db09bd773cb56b8e39f1e98d90820 \
--hash=sha256:b6f00ad5ebe846cc91763b1d0c6d30a8042e02b2316e27b05de04fa6ec831ec5 \
--hash=sha256:887668e792b7edbfb1d3c9d8b5d8c859269a0f0eba4dda562adb95500f60dbba
psycopg2==2.8.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") \
--hash=sha256:068115e13c70dc5982dfc00c5d70437fe37c014c808acce119b5448361c03725 \
--hash=sha256:d160744652e81c80627a909a0e808f3c6653a40af435744de037e3172cf277f5 \
--hash=sha256:b8cae8b2f022efa1f011cc753adb9cbadfa5a184431d09b273fb49b4167561ad \
--hash=sha256:f22ea9b67aea4f4a1718300908a2fb62b3e4276cf00bd829a97ab5894af42ea3 \
--hash=sha256:26e7fd115a6db75267b325de0fba089b911a4a12ebd3d0b5e7acb7028bc46821 \
--hash=sha256:00195b5f6832dbf2876b8bf77f12bdce648224c89c880719c745b90515233301 \
--hash=sha256:a49833abfdede8985ba3f3ec641f771cca215479f41523e99dace96d5b8cce2a \
--hash=sha256:f974c96fca34ae9e4f49839ba6b78addf0346777b46c4da27a7bf54f48d3057d \
--hash=sha256:6a3d9efb6f36f1fe6aa8dbb5af55e067db802502c55a9defa47c5a1dad41df84 \
--hash=sha256:56fee7f818d032f802b8eed81ef0c1232b8b42390df189cab9cfa87573fe52c5 \
--hash=sha256:ad2fe8a37be669082e61fb001c185ffb58867fdbb3e7a6b0b0d2ffe232353a3e \
--hash=sha256:56007a226b8e95aa980ada7abdea6b40b75ce62a433bd27cec7a8178d57f4051 \
--hash=sha256:2c93d4d16933fea5bbacbe1aaf8fa8c1348740b2e50b3735d1b0bf8154cbf0f3 \
--hash=sha256:d5062ae50b222da28253059880a871dc87e099c25cb68acf613d9d227413d6f7 \
--hash=sha256:fb23f6c71107c37fd667cb4ea363ddeb936b348bbd6449278eb92c189699f543
pyasn1-modules==0.2.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e \
--hash=sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199 \
--hash=sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405 \
--hash=sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb \
--hash=sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8 \
--hash=sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74 \
--hash=sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d \
--hash=sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45 \
--hash=sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4 \
--hash=sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811 \
--hash=sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed \
--hash=sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0 \
--hash=sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd
pyasn1==0.4.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3 \
--hash=sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf \
--hash=sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00 \
--hash=sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8 \
--hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \
--hash=sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86 \
--hash=sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7 \
--hash=sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576 \
--hash=sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12 \
--hash=sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2 \
--hash=sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359 \
--hash=sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776 \
--hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba
pycrypto==2.6.1 \
--hash=sha256:f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c
python-dateutil==2.8.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0") \
--hash=sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c \
--hash=sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a
python-ldap==3.3.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \
--hash=sha256:4711cacf013e298754abd70058ccc995758177fb425f1c2d30e71adfc1d00aa5
pytz==2021.1; python_version >= "3.6" and python_version < "4.0" \
--hash=sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798 \
--hash=sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da
six==1.15.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" \
--hash=sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced \
--hash=sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259
smmap==3.0.5; python_version >= "3.4" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.4" \
--hash=sha256:7bfcf367828031dc893530a29cb35eb8c8f2d7c8f2d0989354d75d24c8573714 \
--hash=sha256:84c2751ef3072d4f6b2785ec7ee40244c6f45eb934d9e543e2c51f1bd3d54c50
sqlparse==0.4.1; python_version >= "3.6" and python_version < "4.0" \
--hash=sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0 \
--hash=sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8
zipp==3.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_version < "3.7" and python_full_version >= "3.5.0" \
--hash=sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108 \
--hash=sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb

View file

@ -28,21 +28,10 @@ from __future__ import unicode_literals
from django.contrib import admin from django.contrib import admin
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
from .models import ( from .models import (AssoOption, DocumentTemplate, GeneralOption, HomeOption,
OptionalUser, MailContact, MailMessageOption, OptionalMachine,
OptionalMachine, OptionalTopologie, OptionalUser, RadiusKey, Reminder,
OptionalTopologie, Service, SwitchManagementCred)
GeneralOption,
Service,
MailContact,
AssoOption,
MailMessageOption,
HomeOption,
RadiusKey,
SwitchManagementCred,
Reminder,
DocumentTemplate,
)
class OptionalUserAdmin(VersionAdmin): class OptionalUserAdmin(VersionAdmin):

View file

@ -22,11 +22,12 @@
from rest_framework import serializers from rest_framework import serializers
import preferences.models as preferences import preferences.models as preferences
from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer from api.serializers import (NamespacedHIField, NamespacedHMSerializer,
NamespacedHRField)
class OptionalUserSerializer(NamespacedHMSerializer): class OptionalUserSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.OptionalUser` objects. """Serialize `preferences.models.OptionalUser` objects."""
"""
tel_mandatory = serializers.BooleanField(source="is_tel_mandatory") tel_mandatory = serializers.BooleanField(source="is_tel_mandatory")
shell_default = serializers.StringRelatedField() shell_default = serializers.StringRelatedField()
@ -47,8 +48,7 @@ class OptionalUserSerializer(NamespacedHMSerializer):
class OptionalMachineSerializer(NamespacedHMSerializer): class OptionalMachineSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.OptionalMachine` objects. """Serialize `preferences.models.OptionalMachine` objects."""
"""
class Meta: class Meta:
model = preferences.OptionalMachine model = preferences.OptionalMachine
@ -59,13 +59,12 @@ class OptionalMachineSerializer(NamespacedHMSerializer):
"ipv6_mode", "ipv6_mode",
"create_machine", "create_machine",
"ipv6", "ipv6",
"default_dns_ttl" "default_dns_ttl",
) )
class OptionalTopologieSerializer(NamespacedHMSerializer): class OptionalTopologieSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.OptionalTopologie` objects. """Serialize `preferences.models.OptionalTopologie` objects."""
"""
switchs_management_interface_ip = serializers.CharField() switchs_management_interface_ip = serializers.CharField()
@ -85,8 +84,7 @@ class OptionalTopologieSerializer(NamespacedHMSerializer):
class RadiusOptionSerializer(NamespacedHMSerializer): class RadiusOptionSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.RadiusOption` objects """Serialize `preferences.models.RadiusOption` objects"""
"""
class Meta: class Meta:
model = preferences.RadiusOption model = preferences.RadiusOption
@ -107,8 +105,7 @@ class RadiusOptionSerializer(NamespacedHMSerializer):
class GeneralOptionSerializer(NamespacedHMSerializer): class GeneralOptionSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.GeneralOption` objects. """Serialize `preferences.models.GeneralOption` objects."""
"""
class Meta: class Meta:
model = preferences.GeneralOption model = preferences.GeneralOption
@ -128,8 +125,7 @@ class GeneralOptionSerializer(NamespacedHMSerializer):
class HomeServiceSerializer(NamespacedHMSerializer): class HomeServiceSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.Service` objects. """Serialize `preferences.models.Service` objects."""
"""
class Meta: class Meta:
model = preferences.Service model = preferences.Service
@ -138,8 +134,7 @@ class HomeServiceSerializer(NamespacedHMSerializer):
class AssoOptionSerializer(NamespacedHMSerializer): class AssoOptionSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.AssoOption` objects. """Serialize `preferences.models.AssoOption` objects."""
"""
class Meta: class Meta:
model = preferences.AssoOption model = preferences.AssoOption
@ -157,8 +152,7 @@ class AssoOptionSerializer(NamespacedHMSerializer):
class HomeOptionSerializer(NamespacedHMSerializer): class HomeOptionSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.HomeOption` objects. """Serialize `preferences.models.HomeOption` objects."""
"""
class Meta: class Meta:
model = preferences.HomeOption model = preferences.HomeOption
@ -166,8 +160,7 @@ class HomeOptionSerializer(NamespacedHMSerializer):
class MailMessageOptionSerializer(NamespacedHMSerializer): class MailMessageOptionSerializer(NamespacedHMSerializer):
"""Serialize `preferences.models.MailMessageOption` objects. """Serialize `preferences.models.MailMessageOption` objects."""
"""
class Meta: class Meta:
model = preferences.MailMessageOption model = preferences.MailMessageOption

View file

@ -21,9 +21,7 @@
from . import views from . import views
urls_viewset = [ urls_viewset = [(r"preferences/service", views.HomeServiceViewSet, "homeservice")]
(r"preferences/service", views.HomeServiceViewSet, "homeservice")
]
urls_view = [ urls_view = [
(r"preferences/optionaluser", views.OptionalUserView), (r"preferences/optionaluser", views.OptionalUserView),
@ -33,5 +31,5 @@ urls_view = [
(r"preferences/generaloption", views.GeneralOptionView), (r"preferences/generaloption", views.GeneralOptionView),
(r"preferences/assooption", views.AssoOptionView), (r"preferences/assooption", views.AssoOptionView),
(r"preferences/homeoption", views.HomeOptionView), (r"preferences/homeoption", views.HomeOptionView),
(r"preferences/mailmessageoption", views.MailMessageOptionView) (r"preferences/mailmessageoption", views.MailMessageOptionView),
] ]

View file

@ -19,16 +19,16 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from rest_framework import viewsets, generics from rest_framework import generics, viewsets
from . import serializers
import preferences.models as preferences import preferences.models as preferences
from api.permissions import ACLPermission from api.permissions import ACLPermission
from . import serializers
class OptionalUserView(generics.RetrieveAPIView): class OptionalUserView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.` settings. """Exposes details of `preferences.models.` settings."""
"""
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.OptionalUser.can_view_all]} perms_map = {"GET": [preferences.OptionalUser.can_view_all]}
@ -39,8 +39,7 @@ class OptionalUserView(generics.RetrieveAPIView):
class OptionalMachineView(generics.RetrieveAPIView): class OptionalMachineView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalMachine` settings. """Exposes details of `preferences.models.OptionalMachine` settings."""
"""
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.OptionalMachine.can_view_all]} perms_map = {"GET": [preferences.OptionalMachine.can_view_all]}
@ -51,8 +50,7 @@ class OptionalMachineView(generics.RetrieveAPIView):
class OptionalTopologieView(generics.RetrieveAPIView): class OptionalTopologieView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalTopologie` settings. """Exposes details of `preferences.models.OptionalTopologie` settings."""
"""
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.OptionalTopologie.can_view_all]} perms_map = {"GET": [preferences.OptionalTopologie.can_view_all]}
@ -63,8 +61,7 @@ class OptionalTopologieView(generics.RetrieveAPIView):
class RadiusOptionView(generics.RetrieveAPIView): class RadiusOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalTopologie` settings. """Exposes details of `preferences.models.OptionalTopologie` settings."""
"""
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.RadiusOption.can_view_all]} perms_map = {"GET": [preferences.RadiusOption.can_view_all]}
@ -75,8 +72,7 @@ class RadiusOptionView(generics.RetrieveAPIView):
class GeneralOptionView(generics.RetrieveAPIView): class GeneralOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.GeneralOption` settings. """Exposes details of `preferences.models.GeneralOption` settings."""
"""
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.GeneralOption.can_view_all]} perms_map = {"GET": [preferences.GeneralOption.can_view_all]}
@ -87,16 +83,14 @@ class GeneralOptionView(generics.RetrieveAPIView):
class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet): class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `preferences.models.Service` objects. """Exposes list and details of `preferences.models.Service` objects."""
"""
queryset = preferences.Service.objects.all() queryset = preferences.Service.objects.all()
serializer_class = serializers.HomeServiceSerializer serializer_class = serializers.HomeServiceSerializer
class AssoOptionView(generics.RetrieveAPIView): class AssoOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.AssoOption` settings. """Exposes details of `preferences.models.AssoOption` settings."""
"""
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.AssoOption.can_view_all]} perms_map = {"GET": [preferences.AssoOption.can_view_all]}
@ -107,8 +101,7 @@ class AssoOptionView(generics.RetrieveAPIView):
class HomeOptionView(generics.RetrieveAPIView): class HomeOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.HomeOption` settings. """Exposes details of `preferences.models.HomeOption` settings."""
"""
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.HomeOption.can_view_all]} perms_map = {"GET": [preferences.HomeOption.can_view_all]}
@ -119,8 +112,7 @@ class HomeOptionView(generics.RetrieveAPIView):
class MailMessageOptionView(generics.RetrieveAPIView): class MailMessageOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.MailMessageOption` settings. """Exposes details of `preferences.models.MailMessageOption` settings."""
"""
permission_classes = (ACLPermission,) permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.MailMessageOption.can_view_all]} perms_map = {"GET": [preferences.MailMessageOption.can_view_all]}

11
preferences/apps.py Normal file
View file

@ -0,0 +1,11 @@
"""
Configuration of preferences app.
"""
from django.apps import AppConfig
class PreferencesConfig(AppConfig):
"""Configuration of preferences app."""
name = "preferences"

View file

@ -25,36 +25,22 @@ Forms to edit preferences: users, machines, topology, organisation etc.
from __future__ import unicode_literals from __future__ import unicode_literals
from django.forms import ModelForm, Form
from django.db.models import Q
from django import forms from django import forms
from django.db.models import Q
from django.forms import Form, ModelForm
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from re2o.mixins import FormRevMixin from re2o.mixins import FormRevMixin
from re2o.widgets import ( from re2o.widgets import (AutocompleteModelWidget,
AutocompleteModelWidget, AutocompleteMultipleModelWidget)
AutocompleteMultipleModelWidget
)
from .models import (
OptionalUser,
OptionalMachine,
OptionalTopologie,
GeneralOption,
AssoOption,
MailMessageOption,
HomeOption,
Service,
MailContact,
Reminder,
RadiusKey,
SwitchManagementCred,
RadiusOption,
CotisationsOption,
DocumentTemplate,
RadiusAttribute,
Mandate,
)
from topologie.models import Switch from topologie.models import Switch
from .models import (AssoOption, CotisationsOption, DocumentTemplate,
GeneralOption, HomeOption, MailContact, MailMessageOption,
Mandate, OptionalMachine, OptionalTopologie, OptionalUser,
RadiusAttribute, RadiusKey, RadiusOption, Reminder,
Service, SwitchManagementCred)
class EditOptionalUserForm(ModelForm): class EditOptionalUserForm(ModelForm):
"""Form used to edit user preferences.""" """Form used to edit user preferences."""
@ -74,14 +60,22 @@ class EditOptionalUserForm(ModelForm):
self.fields["self_change_shell"].label = _("Self change shell") self.fields["self_change_shell"].label = _("Self change shell")
self.fields["self_change_pseudo"].label = _("Self change pseudo") self.fields["self_change_pseudo"].label = _("Self change pseudo")
self.fields["self_room_policy"].label = _("Self room policy") self.fields["self_room_policy"].label = _("Self room policy")
self.fields["local_email_accounts_enabled"].label = _("Local email accounts enabled") self.fields["local_email_accounts_enabled"].label = _(
"Local email accounts enabled"
)
self.fields["local_email_domain"].label = _("Local email domain") self.fields["local_email_domain"].label = _("Local email domain")
self.fields["max_email_address"].label = _("Max local email address") self.fields["max_email_address"].label = _("Max local email address")
self.fields["delete_notyetactive"].label = _("Delete not yet active users") self.fields["delete_notyetactive"].label = _("Delete not yet active users")
self.fields["disable_emailnotyetconfirmed"].label = _("Disabled email not yet confirmed") self.fields["disable_emailnotyetconfirmed"].label = _(
"Disabled email not yet confirmed"
)
self.fields["self_adhesion"].label = _("Self registration") self.fields["self_adhesion"].label = _("Self registration")
self.fields["all_users_active"].label = _("All users are state active by default") self.fields["all_users_active"].label = _(
self.fields["allow_set_password_during_user_creation"].label = _("Allow set password during user creation") "All users are state active by default"
)
self.fields["allow_set_password_during_user_creation"].label = _(
"Allow set password during user creation"
)
self.fields["allow_archived_connexion"].label = _("Allow archived connexion") self.fields["allow_archived_connexion"].label = _("Allow archived connexion")
@ -211,9 +205,7 @@ class EditMailMessageOptionForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__) prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(EditMailMessageOptionForm, self).__init__(*args, prefix=prefix, **kwargs) super(EditMailMessageOptionForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields["welcome_mail_fr"].label = _( self.fields["welcome_mail_fr"].label = _("Message for the French welcome email")
"Message for the French welcome email"
)
self.fields["welcome_mail_en"].label = _( self.fields["welcome_mail_en"].label = _(
"Message for the English welcome email" "Message for the English welcome email"
) )
@ -355,8 +347,7 @@ class ServiceForm(ModelForm):
class DelServiceForm(Form): class DelServiceForm(Form):
"""Form used to delete one or several services displayed on the home page. """Form used to delete one or several services displayed on the home page."""
"""
services = forms.ModelMultipleChoiceField( services = forms.ModelMultipleChoiceField(
queryset=Service.objects.none(), queryset=Service.objects.none(),

View file

@ -0,0 +1,65 @@
# Generated by Django 2.2.18 on 2021-02-08 17:27
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('preferences', '0002_foreign_keys'),
]
operations = [
migrations.AlterModelOptions(
name='assooption',
options={'verbose_name': 'organisation preferences'},
),
migrations.AlterModelOptions(
name='generaloption',
options={'verbose_name': 'general preferences'},
),
migrations.AlterModelOptions(
name='homeoption',
options={'verbose_name': 'homepage preferences'},
),
migrations.AlterModelOptions(
name='mailcontact',
options={'verbose_name': 'contact email address', 'verbose_name_plural': 'contact email addresses'},
),
migrations.AlterModelOptions(
name='mailmessageoption',
options={'verbose_name': 'email message preferences'},
),
migrations.AlterModelOptions(
name='mandate',
options={'verbose_name': 'mandate', 'verbose_name_plural': 'mandates'},
),
migrations.AlterModelOptions(
name='optionalmachine',
options={'verbose_name': 'machine preferences'},
),
migrations.AlterModelOptions(
name='optionaltopologie',
options={'verbose_name': 'topology preferences'},
),
migrations.AlterModelOptions(
name='optionaluser',
options={'verbose_name': 'user preferences'},
),
migrations.AlterModelOptions(
name='radiuskey',
options={'verbose_name': 'RADIUS key', 'verbose_name_plural': 'RADIUS keys'},
),
migrations.AlterModelOptions(
name='reminder',
options={'verbose_name': 'reminder', 'verbose_name_plural': 'reminders'},
),
migrations.AlterModelOptions(
name='service',
options={'verbose_name': 'service', 'verbose_name_plural': 'services'},
),
migrations.AlterModelOptions(
name='switchmanagementcred',
options={'verbose_name': 'switch management credentials'},
),
]

View file

@ -25,23 +25,22 @@ Models defining the preferences for users, machines, emails, general settings
etc. etc.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
import os
from django.utils.functional import cached_property import os
from django.utils import timezone from datetime import timedelta
from django.core.cache import cache
from django.db import models from django.db import models
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.core.cache import cache
from django.forms import ValidationError from django.forms import ValidationError
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import machines.models import machines.models
from re2o.mixins import AclMixin, RevMixin
from re2o.aes_field import AESEncryptedField from re2o.aes_field import AESEncryptedField
from re2o.mixins import AclMixin, RevMixin
from datetime import timedelta
class PreferencesModel(models.Model): class PreferencesModel(models.Model):
@ -185,7 +184,6 @@ class OptionalUser(AclMixin, PreferencesModel):
) )
class Meta: class Meta:
permissions = (("view_optionaluser", _("Can view the user preferences")),)
verbose_name = _("user preferences") verbose_name = _("user preferences")
def clean(self): def clean(self):
@ -241,7 +239,6 @@ class OptionalMachine(AclMixin, PreferencesModel):
return not self.get_cached_value("ipv6_mode") == "DISABLED" return not self.get_cached_value("ipv6_mode") == "DISABLED"
class Meta: class Meta:
permissions = (("view_optionalmachine", _("Can view the machine preferences")),)
verbose_name = _("machine preferences") verbose_name = _("machine preferences")
@ -330,7 +327,7 @@ class OptionalTopologie(AclMixin, PreferencesModel):
configuration. configuration.
""" """
if self.switchs_ip_type: if self.switchs_ip_type:
from machines.models import Role, Interface from machines.models import Interface, Role
return ( return (
Interface.objects.filter( Interface.objects.filter(
@ -366,14 +363,14 @@ class OptionalTopologie(AclMixin, PreferencesModel):
"""Get the dictionary of IP addresses for the configuration of """Get the dictionary of IP addresses for the configuration of
switches. switches.
""" """
from machines.models import Role, Ipv6List, Interface from machines.models import Interface, Ipv6List, Role
def return_ips_dict(interfaces): def return_ips_dict(interfaces):
return { return {
"ipv4": [str(interface.ipv4) for interface in interfaces], "ipv4": [str(interface.ipv4) for interface in interfaces],
"ipv6": Ipv6List.objects.filter(interface__in=interfaces).filter(active=True).values_list( "ipv6": Ipv6List.objects.filter(interface__in=interfaces)
"ipv6", flat=True .filter(active=True)
), .values_list("ipv6", flat=True),
} }
ntp_servers = Role.all_interfaces_for_roletype("ntp-server").filter( ntp_servers = Role.all_interfaces_for_roletype("ntp-server").filter(
@ -421,9 +418,6 @@ class OptionalTopologie(AclMixin, PreferencesModel):
) )
class Meta: class Meta:
permissions = (
("view_optionaltopologie", _("Can view the topology preferences")),
)
verbose_name = _("topology preferences") verbose_name = _("topology preferences")
@ -453,7 +447,6 @@ class RadiusKey(AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = (("view_radiuskey", _("Can view a RADIUS key object")),)
verbose_name = _("RADIUS key") verbose_name = _("RADIUS key")
verbose_name_plural = _("RADIUS keys") verbose_name_plural = _("RADIUS keys")
@ -483,12 +476,6 @@ class SwitchManagementCred(AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = (
(
"view_switchmanagementcred",
_("Can view a switch management credentials object"),
),
)
verbose_name = _("switch management credentials") verbose_name = _("switch management credentials")
def __str__(self): def __str__(self):
@ -518,7 +505,6 @@ class Reminder(AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = (("view_reminder", _("Can view a reminder object")),)
verbose_name = _("reminder") verbose_name = _("reminder")
verbose_name_plural = _("reminders") verbose_name_plural = _("reminders")
@ -582,7 +568,6 @@ class GeneralOption(AclMixin, PreferencesModel):
GTU = models.FileField(upload_to="", default="", null=True, blank=True) GTU = models.FileField(upload_to="", default="", null=True, blank=True)
class Meta: class Meta:
permissions = (("view_generaloption", _("Can view the general preferences")),)
verbose_name = _("general preferences") verbose_name = _("general preferences")
@ -609,7 +594,6 @@ class Service(AclMixin, models.Model):
image = models.ImageField(upload_to="logo", blank=True) image = models.ImageField(upload_to="logo", blank=True)
class Meta: class Meta:
permissions = (("view_service", _("Can view the service preferences")),)
verbose_name = _("service") verbose_name = _("service")
verbose_name_plural = _("services") verbose_name_plural = _("services")
@ -641,9 +625,6 @@ class MailContact(AclMixin, models.Model):
return self.address.split("@")[0] return self.address.split("@")[0]
class Meta: class Meta:
permissions = (
("view_mailcontact", _("Can view a contact email address object")),
)
verbose_name = _("contact email address") verbose_name = _("contact email address")
verbose_name_plural = _("contact email addresses") verbose_name_plural = _("contact email addresses")
@ -664,7 +645,6 @@ class Mandate(RevMixin, AclMixin, models.Model):
class Meta: class Meta:
verbose_name = _("mandate") verbose_name = _("mandate")
verbose_name_plural = _("mandates") verbose_name_plural = _("mandates")
permissions = (("view_mandate", _("Can view a mandate object")),)
president = models.ForeignKey( president = models.ForeignKey(
"users.User", "users.User",
@ -679,7 +659,7 @@ class Mandate(RevMixin, AclMixin, models.Model):
@classmethod @classmethod
def get_mandate(cls, date=timezone.now): def get_mandate(cls, date=timezone.now):
""""Get the mandate taking place at the given date. """ "Get the mandate taking place at the given date.
Args: Args:
date: the date used to find the mandate (default: timezone.now). date: the date used to find the mandate (default: timezone.now).
@ -740,7 +720,6 @@ class AssoOption(AclMixin, PreferencesModel):
description = models.TextField(null=True, blank=True) description = models.TextField(null=True, blank=True)
class Meta: class Meta:
permissions = (("view_assooption", _("Can view the organisation preferences")),)
verbose_name = _("organisation preferences") verbose_name = _("organisation preferences")
@ -766,7 +745,6 @@ class HomeOption(AclMixin, PreferencesModel):
twitter_account_name = models.CharField(max_length=32, null=True, blank=True) twitter_account_name = models.CharField(max_length=32, null=True, blank=True)
class Meta: class Meta:
permissions = (("view_homeoption", _("Can view the homepage preferences")),)
verbose_name = _("homepage preferences") verbose_name = _("homepage preferences")
@ -793,9 +771,6 @@ class MailMessageOption(AclMixin, models.Model):
) )
class Meta: class Meta:
permissions = (
("view_mailmessageoption", _("Can view the email message preferences")),
)
verbose_name = _("email message preferences") verbose_name = _("email message preferences")

View file

@ -25,139 +25,134 @@ Urls de l'application preferences, pointant vers les fonctions de views
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url from django.urls import path, re_path
from . import views from . import views
from .views import edit_options from .views import edit_options
app_name = "preferences"
urlpatterns = [ urlpatterns = [
url( re_path(
r"^edit_options/(?P<section>OptionalUser)$", r"^edit_options/(?P<section>OptionalUser)$",
edit_options, edit_options,
name="edit-options", name="edit-options",
), ),
url( re_path(
r"^edit_options/(?P<section>OptionalMachine)$", r"^edit_options/(?P<section>OptionalMachine)$",
edit_options, edit_options,
name="edit-options", name="edit-options",
), ),
url( re_path(
r"^edit_options/(?P<section>OptionalTopologie)$", r"^edit_options/(?P<section>OptionalTopologie)$",
edit_options, edit_options,
name="edit-options", name="edit-options",
), ),
url( re_path(
r"^edit_options/(?P<section>GeneralOption)$", r"^edit_options/(?P<section>GeneralOption)$",
edit_options, edit_options,
name="edit-options", name="edit-options",
), ),
url( re_path(
r"^edit_options/(?P<section>AssoOption)$", r"^edit_options/(?P<section>AssoOption)$",
edit_options, edit_options,
name="edit-options", name="edit-options",
), ),
url( re_path(
r"^edit_options/(?P<section>HomeOption)$", r"^edit_options/(?P<section>HomeOption)$",
edit_options, edit_options,
name="edit-options", name="edit-options",
), ),
url( re_path(
r"^edit_options/(?P<section>MailMessageOption)$", r"^edit_options/(?P<section>MailMessageOption)$",
edit_options, edit_options,
name="edit-options", name="edit-options",
), ),
url( re_path(
r"^edit_options/(?P<section>RadiusOption)$", r"^edit_options/(?P<section>RadiusOption)$",
edit_options, edit_options,
name="edit-options", name="edit-options",
), ),
url( re_path(
r"^edit_options/(?P<section>CotisationsOption)$", r"^edit_options/(?P<section>CotisationsOption)$",
edit_options, edit_options,
name="edit-options", name="edit-options",
), ),
url(r"^add_service/$", views.add_service, name="add-service"), path("add_service", views.add_service, name="add-service"),
url( path("edit_service/<int:serviceid>", views.edit_service, name="edit-service"),
r"^edit_service/(?P<serviceid>[0-9]+)$", views.edit_service, name="edit-service" path("del_service/<int:serviceid>", views.del_service, name="del-service"),
), path("add_mailcontact", views.add_mailcontact, name="add-mailcontact"),
url(r"^del_service/(?P<serviceid>[0-9]+)$", views.del_service, name="del-service"), path(
url(r"^add_mailcontact/$", views.add_mailcontact, name="add-mailcontact"), "edit_mailcontact/<int:mailcontactid>",
url(
r"^edit_mailcontact/(?P<mailcontactid>[0-9]+)$",
views.edit_mailcontact, views.edit_mailcontact,
name="edit-mailcontact", name="edit-mailcontact",
), ),
url(r"^del_mailcontact/$", views.del_mailcontact, name="del-mailcontact"), path("del_mailcontact", views.del_mailcontact, name="del-mailcontact"),
url(r"^add_reminder/$", views.add_reminder, name="add-reminder"), path("add_reminder", views.add_reminder, name="add-reminder"),
url( path(
r"^edit_reminder/(?P<reminderid>[0-9]+)$", "edit_reminder/<int:reminderid>",
views.edit_reminder, views.edit_reminder,
name="edit-reminder", name="edit-reminder",
), ),
url( path(
r"^del_reminder/(?P<reminderid>[0-9]+)$", "del_reminder/<int:reminderid>",
views.del_reminder, views.del_reminder,
name="del-reminder", name="del-reminder",
), ),
url(r"^add_radiuskey/$", views.add_radiuskey, name="add-radiuskey"), path("add_radiuskey", views.add_radiuskey, name="add-radiuskey"),
url( path(
r"^edit_radiuskey/(?P<radiuskeyid>[0-9]+)$", "edit_radiuskey/<int:radiuskeyid>",
views.edit_radiuskey, views.edit_radiuskey,
name="edit-radiuskey", name="edit-radiuskey",
), ),
url( path(
r"^del_radiuskey/(?P<radiuskeyid>[0-9]+)$", "del_radiuskey/<int:radiuskeyid>",
views.del_radiuskey, views.del_radiuskey,
name="del-radiuskey", name="del-radiuskey",
), ),
url( path(
r"^add_switchmanagementcred/$", "add_switchmanagementcred",
views.add_switchmanagementcred, views.add_switchmanagementcred,
name="add-switchmanagementcred", name="add-switchmanagementcred",
), ),
url( path(
r"^edit_switchmanagementcred/(?P<switchmanagementcredid>[0-9]+)$", "edit_switchmanagementcred/<int:switchmanagementcredid>",
views.edit_switchmanagementcred, views.edit_switchmanagementcred,
name="edit-switchmanagementcred", name="edit-switchmanagementcred",
), ),
url( path(
r"^del_switchmanagementcred/(?P<switchmanagementcredid>[0-9]+)$", "del_switchmanagementcred/<int:switchmanagementcredid>",
views.del_switchmanagementcred, views.del_switchmanagementcred,
name="del-switchmanagementcred", name="del-switchmanagementcred",
), ),
url( path(
r"^add_document_template/$", "add_document_template",
views.add_document_template, views.add_document_template,
name="add-document-template", name="add-document-template",
), ),
url( path(
r"^edit_document_template/(?P<documenttemplateid>[0-9]+)$", "edit_document_template/<int:documenttemplateid>",
views.edit_document_template, views.edit_document_template,
name="edit-document-template", name="edit-document-template",
), ),
url( path(
r"^del_document_template/$", "del_document_template",
views.del_document_template, views.del_document_template,
name="del-document-template", name="del-document-template",
), ),
url(r"^add_mandate/$", views.add_mandate, name="add-mandate"), path("add_mandate", views.add_mandate, name="add-mandate"),
url( path("edit_mandate/<int:mandateid>", views.edit_mandate, name="edit-mandate"),
r"^edit_mandate/(?P<mandateid>[0-9]+)$", views.edit_mandate, name="edit-mandate" path("del_mandate/<int:mandateid>", views.del_mandate, name="del-mandate"),
), path("add_radiusattribute", views.add_radiusattribute, name="add-radiusattribute"),
url(r"^del_mandate/(?P<mandateid>[0-9]+)$", views.del_mandate, name="del-mandate"), path(
url( "edit_radiusattribute/<int:radiusattributeid>",
r"^add_radiusattribute/$", views.add_radiusattribute, name="add-radiusattribute"
),
url(
r"^edit_radiusattribute/(?P<radiusattributeid>[0-9]+)$",
views.edit_radiusattribute, views.edit_radiusattribute,
name="edit-radiusattribute", name="edit-radiusattribute",
), ),
url( path(
r"^del_radiusattribute/(?P<radiusattributeid>[0-9]+)$", "del_radiusattribute/<int:radiusattributeid>",
views.del_radiusattribute, views.del_radiusattribute,
name="del-radiusattribute", name="del-radiusattribute",
), ),
url(r"^$", views.display_options, name="display-options"), path("", views.display_options, name="display-options"),
] ]

View file

@ -30,61 +30,33 @@ services etc.)
from __future__ import unicode_literals from __future__ import unicode_literals
from django.urls import reverse from importlib import import_module
from django.shortcuts import redirect
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.db.models import ProtectedError
from django.db import transaction from django.db import transaction
from django.db.models import ProtectedError
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from reversion import revisions as reversion from reversion import revisions as reversion
from importlib import import_module from re2o.acl import (acl_error_message, can_create, can_delete,
can_delete_set, can_edit, can_view_all)
from re2o.settings_local import OPTIONNAL_APPS_RE2O from re2o.settings_local import OPTIONNAL_APPS_RE2O
from re2o.views import form from re2o.views import form
from re2o.acl import (
can_create,
can_edit,
can_delete_set,
can_view_all,
can_delete,
acl_error_message,
)
from .forms import MailContactForm, DelMailContactForm from . import forms, models
from .forms import ( from .forms import (DelDocumentTemplateForm, DelMailContactForm,
ServiceForm, DelRadiusAttributeForm, DocumentTemplateForm,
ReminderForm, MailContactForm, MandateForm, RadiusAttributeForm,
RadiusKeyForm, RadiusKeyForm, ReminderForm, ServiceForm,
SwitchManagementCredForm, SwitchManagementCredForm)
DocumentTemplateForm, from .models import (AssoOption, CotisationsOption, DocumentTemplate,
DelDocumentTemplateForm, GeneralOption, HomeOption, MailContact, MailMessageOption,
RadiusAttributeForm, Mandate, OptionalMachine, OptionalTopologie, OptionalUser,
DelRadiusAttributeForm, RadiusAttribute, RadiusKey, RadiusOption, Reminder,
MandateForm, Service, SwitchManagementCred)
)
from .models import (
Service,
MailContact,
OptionalUser,
OptionalMachine,
AssoOption,
MailMessageOption,
GeneralOption,
OptionalTopologie,
HomeOption,
Reminder,
RadiusKey,
SwitchManagementCred,
RadiusOption,
CotisationsOption,
DocumentTemplate,
RadiusAttribute,
Mandate,
)
from . import models
from . import forms
def edit_options_template_function(request, section, forms, models): def edit_options_template_function(request, section, forms, models):

73
pyproject.toml Normal file
View file

@ -0,0 +1,73 @@
[tool.poetry]
name = "re2o"
version = "2.9.0"
description = "Re2o is a management software initially developed at Rézo Metz. It is now in use in several student organizations. It aims to remain agnostic of the organization that uses it and be easy to setup."
authors = [
"Gabriel Detraz",
"Hugo Levy-falk",
"Maël Kervella",
"Jean-romain Garnier",
"Arthur Grisel-davy",
"Laouen Fernet",
"Augustin Lemesle",
"Lara Kermarec",
"Alexandre Iooss",
"Yoann Piétri",
"Charlie Jacomme",
"Corentin Canebier",
"Bombar Maxime",
"Guillaume Goessel",
"Matthieu Michelet",
"Edpibu",
"Fardale",
"Jean-marie Mineau",
"David Sinquin",
"Gabriel Le Bouder",
"Simon Brélivet",
"Benjamin Graillot",
"Leïla Bekaddour",
"Éloi Alain",
"Pierre Cadart",
"Antoine Vintache",
"Thibault De Boutray",
"Delphine Salvy",
"Joanne Steiner",
"Krokmou",
"B",
"Daniel Stan",
"Gwenael Le Hir",
"Hugo Hervieux",
"Mikachu",
"Nymous",
"Pierre-antoine Comby",
"Vincent Le Gallic",
]
[tool.poetry.dependencies]
python = ">=3.6,<4.0"
Django = "2.2.18"
django-autocomplete-light = "^3.8.1"
django-bootstrap3 = "^14.2.0"
django-rest-framework = "^0.1.0"
django-reversion = "^3.0.9"
Pillow = "^8.1.0"
python-dateutil = "^2.8.1"
django-macaddress = "^1.7.0"
pycrypto = "^2.6.1"
GitPython = "^3.1.13"
mysqlclient = {version = "^2.0.3", optional=true}
psycopg2 = {version = "^2.8.6", optional=true}
django-ldapdb = {version = "^1.5.1", optional=true}
[tool.poetry.extras]
mysql = ["mysqlclient"]
postgresql = ["psycopg2"]
ldap = ["django-ldapdb"]
[tool.poetry.dev-dependencies]
Sphinx = "^3.4.3"
sphinx-rtd-theme = "^0.5.1"
black = "^20.8b1"
rope = "^0.18.0"
coverage = "^5.4"
docstr-coverage = "^2.0.0"

View file

@ -30,8 +30,8 @@ from __future__ import unicode_literals
import sys import sys
from itertools import chain from itertools import chain
from django.db.models import Model
from django.contrib import messages from django.contrib import messages
from django.db.models import Model
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _

View file

@ -28,22 +28,22 @@ Module defining a AESEncryptedField object that can be used in forms
to handle the use of properly encrypting and decrypting AES keys to handle the use of properly encrypting and decrypting AES keys
""" """
import string
import binascii import binascii
import string
from random import choice from random import choice
from Crypto.Cipher import AES
from django.db import models from Crypto.Cipher import AES
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.db import models
EOD_asbyte = b"`%EofD%`" # This should be something that will not occur in strings EOD_asbyte = b"`%EofD%`" # This should be something that will not occur in strings
EOD = EOD_asbyte.decode("utf-8") EOD = EOD_asbyte.decode("utf-8")
def genstring(length=16, chars=string.printable): def genstring(length=16, chars=string.printable):
""" Generate a random string of length `length` and composed of """Generate a random string of length `length` and composed of
the characters in `chars` """ the characters in `chars`"""
return "".join([choice(chars) for i in range(length)]) return "".join([choice(chars) for i in range(length)])
@ -71,8 +71,8 @@ class AESEncryptedFormField(forms.CharField):
class AESEncryptedField(models.CharField): class AESEncryptedField(models.CharField):
""" A Field that can be used in forms for adding the support """A Field that can be used in forms for adding the support
of AES ecnrypted fields """ of AES ecnrypted fields"""
def save_form_data(self, instance, data): def save_form_data(self, instance, data):
setattr( setattr(

11
re2o/apps.py Normal file
View file

@ -0,0 +1,11 @@
"""
Configuration of re2o app.
"""
from django.apps import AppConfig
class Re2oConfig(AppConfig):
"""Configuration of re2o app."""
name = "re2o"

View file

@ -26,12 +26,11 @@ Global independant usefull functions
import smtplib import smtplib
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from re2o.settings import EMAIL_HOST from re2o.settings import EMAIL_HOST
# Mapping of srtftime format for better understanding # Mapping of srtftime format for better understanding
# https://docs.python.org/3.6/library/datetime.html#strftime-strptime-behavior # https://docs.python.org/3.6/library/datetime.html#strftime-strptime-behavior
datetime_mapping = { datetime_mapping = {
@ -64,7 +63,7 @@ datetime_mapping = {
def smtp_check(local_part): def smtp_check(local_part):
"""Return True if the local_part is already taken """Return True if the local_part is already taken
False if available""" False if available"""
try: try:
srv = smtplib.SMTP(EMAIL_HOST) srv = smtplib.SMTP(EMAIL_HOST)
srv.putcmd("vrfy", local_part) srv.putcmd("vrfy", local_part)
@ -108,9 +107,9 @@ def get_input_formats_help_text(input_formats):
class SortTable: class SortTable:
""" Class gathering uselful stuff to sort the colums of a table, according """Class gathering uselful stuff to sort the colums of a table, according
to the column and order requested. It's used with a dict of possible to the column and order requested. It's used with a dict of possible
values and associated model_fields """ values and associated model_fields"""
# All the possible possible values # All the possible possible values
# The naming convention is based on the URL or the views function # The naming convention is based on the URL or the views function
@ -228,8 +227,8 @@ class SortTable:
@staticmethod @staticmethod
def sort(request, col, order, values): def sort(request, col, order, values):
""" Check if the given values are possible and add .order_by() and """Check if the given values are possible and add .order_by() and
a .reverse() as specified according to those values """ a .reverse() as specified according to those values"""
fields = values.get(col, None) fields = values.get(col, None)
if not fields: if not fields:
fields = values.get("default", []) fields = values.get("default", [])

View file

@ -24,21 +24,22 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime import datetime
from importlib import import_module
from django.contrib import messages from django.contrib import messages
from django.contrib.messages import get_messages from django.contrib.messages import get_messages
from django.http import HttpRequest from django.http import HttpRequest
from preferences.models import GeneralOption, OptionalMachine
from django.utils.translation import get_language from django.utils.translation import get_language
from importlib import import_module
from preferences.models import GeneralOption, OptionalMachine
from re2o.settings_local import OPTIONNAL_APPS_RE2O from re2o.settings_local import OPTIONNAL_APPS_RE2O
def context_user(request): def context_user(request):
"""Global Context function """Global Context function
Returns: Returns:
dict:Containing user's interfaces and himself if logged, else None dict:Containing user's interfaces and himself if logged, else None
""" """
user = request.user user = request.user
@ -51,9 +52,11 @@ def context_user(request):
if global_message not in [msg.message for msg in get_messages(request)]: if global_message not in [msg.message for msg in get_messages(request)]:
messages.warning(request, global_message) messages.warning(request, global_message)
else: else:
if global_message not in [msg.message for msg in get_messages(request._request)]: if global_message not in [
msg.message for msg in get_messages(request._request)
]:
messages.warning(request._request, global_message) messages.warning(request._request, global_message)
if user.is_authenticated(): if user.is_authenticated:
interfaces = user.user_interfaces() interfaces = user.user_interfaces()
else: else:
interfaces = None interfaces = None
@ -70,9 +73,9 @@ def context_user(request):
def context_optionnal_apps(request): def context_optionnal_apps(request):
"""Context functions. Called to add optionnal apps buttons in navbari """Context functions. Called to add optionnal apps buttons in navbari
Returns: Returns:
dict:Containing optionnal template list of functions for navbar found dict:Containing optionnal template list of functions for navbar found
in optional apps in optional apps
""" """
optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O] optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O]

View file

@ -4,44 +4,44 @@ A list of the proud contributors to Re2o
""" """
CONTRIBUTORS = [ CONTRIBUTORS = [
'Gabriel Detraz', "Gabriel Detraz",
'Hugo Levy-falk', "Hugo Levy-falk",
'Maël Kervella', "Maël Kervella",
'Jean-romain Garnier', "Jean-romain Garnier",
'Arthur Grisel-davy', "Arthur Grisel-davy",
'Laouen Fernet', "Laouen Fernet",
'Augustin Lemesle', "Augustin Lemesle",
'Lara Kermarec', "Lara Kermarec",
'Root `root` Root', "Root `root` Root",
'Alexandre Iooss', "Alexandre Iooss",
'Yoann Piétri', "Yoann Piétri",
'Charlie Jacomme', "Charlie Jacomme",
'Corentin Canebier', "Corentin Canebier",
'Bombar Maxime', "Bombar Maxime",
'Guillaume Goessel', "Guillaume Goessel",
'Matthieu Michelet', "Matthieu Michelet",
'Edpibu', "Edpibu",
'Fardale', "Fardale",
'Jean-marie Mineau', "Jean-marie Mineau",
'David Sinquin', "David Sinquin",
'Gabriel Le Bouder', "Gabriel Le Bouder",
'Simon Brélivet', "Simon Brélivet",
'~anonymised~', "~anonymised~",
'Benjamin Graillot', "Benjamin Graillot",
'Leïla Bekaddour', "Leïla Bekaddour",
'Éloi Alain', "Éloi Alain",
'Pierre Cadart', "Pierre Cadart",
'Antoine Vintache', "Antoine Vintache",
'Thibault De Boutray', "Thibault De Boutray",
'Delphine Salvy', "Delphine Salvy",
'Joanne Steiner', "Joanne Steiner",
'Krokmou', "Krokmou",
'B', "B",
'Daniel Stan', "Daniel Stan",
'Gwenael Le Hir', "Gwenael Le Hir",
'Hugo Hervieux', "Hugo Hervieux",
'Mikachu', "Mikachu",
'Nymous', "Nymous",
'Pierre-antoine Comby', "Pierre-antoine Comby",
'Vincent Le Gallic', "Vincent Le Gallic",
] ]

View file

@ -40,8 +40,8 @@ class FieldPermissionModelMixin:
FIELD_PERMISSION_MISSING_DEFAULT = True FIELD_PERMISSION_MISSING_DEFAULT = True
def has_field_perm(self, user, field): def has_field_perm(self, user, field):
""" Checks if a `user` has the right to edit the `field` """Checks if a `user` has the right to edit the `field`
of this model """ of this model"""
if field in self.field_permissions: if field in self.field_permissions:
checks = self.field_permissions[field] checks = self.field_permissions[field]
if not isinstance(checks, (list, tuple)): if not isinstance(checks, (list, tuple)):

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