From b25137acf4fa61e3d96e7d836944b063be8eb6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Fri, 13 Apr 2018 18:58:29 +0000 Subject: [PATCH 01/22] Pep8 compliance on cotisations --- cotisations/acl.py | 6 +- cotisations/forms.py | 44 +++++++--- cotisations/models.py | 124 +++++++++++++++++---------- cotisations/payment.py | 26 ++++-- cotisations/payment_utils/comnpay.py | 53 +++++++----- cotisations/tex.py | 13 +-- cotisations/urls.py | 13 ++- cotisations/views.py | 66 +++++++++----- 8 files changed, 224 insertions(+), 121 deletions(-) diff --git a/cotisations/acl.py b/cotisations/acl.py index 4e147a82..aa98c32a 100644 --- a/cotisations/acl.py +++ b/cotisations/acl.py @@ -27,6 +27,7 @@ Here are defined some functions to check acl on the application. """ from django.utils.translation import ugettext as _ + def can_view(user): """Check if an user can view the application. @@ -38,4 +39,7 @@ def can_view(user): viewing is granted and msg is a message (can be None). """ can = user.has_module_perms('cotisations') - return can, None if can else _("You don't have the rights to see this application.") + if can: + return can, None + else: + return can, _("You don't have the rights to see this application.") diff --git a/cotisations/forms.py b/cotisations/forms.py index d0a891bd..4414de3e 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -20,7 +20,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ -Forms for the 'cotisation' app of re2o. It highly depends on +Forms for the 'cotisation' app of re2o. It highly depends on :cotisations:models and is mainly used by :cotisations:views. The following forms are mainly used to create, edit or delete @@ -38,7 +38,7 @@ from __future__ import unicode_literals from django import forms from django.db.models import Q from django.forms import ModelForm, Form -from django.core.validators import MinValueValidator,MaxValueValidator +from django.core.validators import MinValueValidator, MaxValueValidator from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy as _l @@ -47,7 +47,8 @@ from preferences.models import OptionalUser from users.models import User from re2o.field_permissions import FieldPermissionFormMixin -from re2o.mixins import FormRevMixin +from re2o.mixins import FormRevMixin + class NewFactureForm(FormRevMixin, ModelForm): """ @@ -109,12 +110,16 @@ class CreditSoldeForm(NewFactureForm): montant = forms.DecimalField(max_digits=5, decimal_places=2, required=True) -class SelectUserArticleForm(FormRevMixin, Form): +class SelectUserArticleForm( + FormRevMixin, Form): """ - Form used to select an article during the creation of an invoice for a member. + Form used to select an article during the creation of an invoice for a + member. """ article = forms.ModelChoiceField( - queryset=Article.objects.filter(Q(type_user='All') | Q(type_user='Adherent')), + queryset=Article.objects.filter( + Q(type_user='All') | Q(type_user='Adherent') + ), label=_l("Article"), required=True ) @@ -127,10 +132,13 @@ class SelectUserArticleForm(FormRevMixin, Form): class SelectClubArticleForm(Form): """ - Form used to select an article during the creation of an invoice for a club. + Form used to select an article during the creation of an invoice for a + club. """ article = forms.ModelChoiceField( - queryset=Article.objects.filter(Q(type_user='All') | Q(type_user='Club')), + queryset=Article.objects.filter( + Q(type_user='All') | Q(type_user='Club') + ), label=_l("Article"), required=True ) @@ -140,6 +148,7 @@ class SelectClubArticleForm(Form): required=True ) + # TODO : change Facture to Invoice class NewFactureFormPdf(Form): """ @@ -147,9 +156,18 @@ class NewFactureFormPdf(Form): """ paid = forms.BooleanField(label=_l("Paid"), required=False) # TODO : change dest field to recipient - dest = forms.CharField(required=True, max_length=255, label=_l("Recipient")) + dest = forms.CharField( + required=True, + max_length=255, + label=_l("Recipient") + ) # TODO : change chambre field to address - chambre = forms.CharField(required=False, max_length=10, label=_l("Address")) + chambre = forms.CharField( + required=False, + max_length=10, + label=_l("Address") + ) + # TODO : change Facture to Invoice class EditFactureForm(FieldPermissionFormMixin, NewFactureForm): @@ -313,7 +331,6 @@ class NewFactureSoldeForm(NewFactureForm): # TODO : change paiement to payment and baque to bank fields = ['paiement', 'banque'] - def clean(self): cleaned_data = super(NewFactureSoldeForm, self).clean() # TODO : change paiement to payment @@ -342,7 +359,7 @@ class RechargeForm(FormRevMixin, Form): value = forms.FloatField( label=_l("Amount"), min_value=0.01, - validators = [] + validators=[] ) def __init__(self, *args, **kwargs): @@ -360,7 +377,8 @@ class RechargeForm(FormRevMixin, Form): ) } ) - if value + self.user.solde > OptionalUser.get_cached_value('max_solde'): + if value + self.user.solde > \ + OptionalUser.get_cached_value('max_solde'): raise forms.ValidationError( _("Requested amount is too high. Your balance can't exceed \ %(max_online_balance)s €.") % { diff --git a/cotisations/models.py b/cotisations/models.py index 082847e7..c7b940d6 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -54,7 +54,7 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): """ The model for an invoice. It reprensents the fact that a user paid for something (it can be multiple article paid at once). - + An invoice is linked to : * one or more purchases (one for each article sold that time) * a user (the one who bought those articles) @@ -105,11 +105,16 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): abstract = False permissions = ( # TODO : change facture to invoice - ('change_facture_control', _l("Can change the \"controlled\" state")), - # TODO : seems more likely to be call create_facture_pdf or create_invoice_pdf - ('change_facture_pdf', _l("Can create a custom PDF invoice")), - ('view_facture', _l("Can see an invoice's details")), - ('change_all_facture', _l("Can edit all the previous invoices")), + ('change_facture_control', + _l("Can change the \"controlled\" state")), + # TODO : seems more likely to be call create_facture_pdf + # or create_invoice_pdf + ('change_facture_pdf', + _l("Can create a custom PDF invoice")), + ('view_facture', + _l("Can see an invoice's details")), + ('change_all_facture', + _l("Can edit all the previous invoices")), ) verbose_name = _l("Invoice") verbose_name_plural = _l("Invoices") @@ -159,11 +164,14 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): def can_edit(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.change_facture'): return False, _("You don't have the right to edit an invoice.") - elif not user_request.has_perm('cotisations.change_all_facture') and not self.user.can_edit(user_request, *args, **kwargs)[0]: - return False, _("You don't have the right to edit this user's invoices.") - elif not user_request.has_perm('cotisations.change_all_facture') and\ - (self.control or not self.valid): - return False, _("You don't have the right to edit an invoice already controlled or invalidated.") + elif not user_request.has_perm('cotisations.change_all_facture') and \ + not self.user.can_edit(user_request, *args, **kwargs)[0]: + return False, _("You don't have the right to edit this user's " + "invoices.") + elif not user_request.has_perm('cotisations.change_all_facture') and \ + (self.control or not self.valid): + return False, _("You don't have the right to edit an invoice " + "already controlled or invalidated.") else: return True, None @@ -171,16 +179,19 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): if not user_request.has_perm('cotisations.delete_facture'): return False, _("You don't have the right to delete an invoice.") if not self.user.can_edit(user_request, *args, **kwargs)[0]: - return False, _("You don't have the right to delete this user's invoices.") + return False, _("You don't have the right to delete this user's " + "invoices.") if self.control or not self.valid: - return False, _("You don't have the right to delete an invoice already controlled or invalidated.") + return False, _("You don't have the right to delete an invoice " + "already controlled or invalidated.") else: return True, None def can_view(self, user_request, *args, **kwargs): - if not user_request.has_perm('cotisations.view_facture') and\ - self.user != user_request: - return False, _("You don't have the right to see someone else's invoices history.") + if not user_request.has_perm('cotisations.view_facture') and \ + self.user != user_request: + return False, _("You don't have the right to see someone else's " + "invoices history.") elif not self.valid: return False, _("The invoice has been invalidated.") else: @@ -188,16 +199,22 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): @staticmethod def can_change_control(user_request, *args, **kwargs): - return user_request.has_perm('cotisations.change_facture_control'), _("You don't have the right to edit the \"controlled\" state.") + return ( + user_request.has_perm('cotisations.change_facture_control'), + _("You don't have the right to edit the \"controlled\" state.") + ) @staticmethod def can_change_pdf(user_request, *args, **kwargs): - return user_request.has_perm('cotisations.change_facture_pdf'), _("You don't have the right to edit an invoice.") + return ( + user_request.has_perm('cotisations.change_facture_pdf'), + _("You don't have the right to edit an invoice.") + ) def __init__(self, *args, **kwargs): super(Facture, self).__init__(*args, **kwargs) self.field_permissions = { - 'control' : self.can_change_control, + 'control': self.can_change_control, } def __str__(self): @@ -228,7 +245,7 @@ class Vente(RevMixin, AclMixin, models.Model): """ The model defining a purchase. It consist of one type of article being sold. In particular there may be multiple purchases in a single invoice. - + It's reprensentated by: * an amount (the number of items sold) * an invoice (whose the purchase is part of) @@ -289,7 +306,6 @@ class Vente(RevMixin, AclMixin, models.Model): verbose_name = _l("Purchase") verbose_name_plural = _l("Purchases") - # TODO : change prix_total to total_price def prix_total(self): """ @@ -323,11 +339,13 @@ class Vente(RevMixin, AclMixin, models.Model): facture__in=Facture.objects.filter( user=self.facture.user ).exclude(valid=False)) - ).filter(Q(type_cotisation='All') | Q(type_cotisation=self.type_cotisation) + ).filter( + Q(type_cotisation='All') | + Q(type_cotisation=self.type_cotisation) ).filter( date_start__lt=date_start ).aggregate(Max('date_end'))['date_end__max'] - elif self.type_cotisation=="Adhesion": + elif self.type_cotisation == "Adhesion": end_cotisation = self.facture.user.end_adhesion() else: end_cotisation = self.facture.user.end_connexion() @@ -357,11 +375,15 @@ class Vente(RevMixin, AclMixin, models.Model): def can_edit(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.change_vente'): return False, _("You don't have the right to edit the purchases.") - elif not user_request.has_perm('cotisations.change_all_facture') and not self.facture.user.can_edit(user_request, *args, **kwargs)[0]: - return False, _("You don't have the right to edit this user's purchases.") - elif not user_request.has_perm('cotisations.change_all_vente') and\ - (self.facture.control or not self.facture.valid): - return False, _("You don't have the right to edit a purchase already controlled or invalidated.") + elif not user_request.has_perm('cotisations.change_all_facture') and \ + not self.facture.user.can_edit( + user_request, *args, **kwargs)[0]: + return False, _("You don't have the right to edit this user's " + "purchases.") + elif not user_request.has_perm('cotisations.change_all_vente') and \ + (self.facture.control or not self.facture.valid): + return False, _("You don't have the right to edit a purchase " + "already controlled or invalidated.") else: return True, None @@ -369,16 +391,19 @@ class Vente(RevMixin, AclMixin, models.Model): if not user_request.has_perm('cotisations.delete_vente'): return False, _("You don't have the right to delete a purchase.") if not self.facture.user.can_edit(user_request, *args, **kwargs)[0]: - return False, _("You don't have the right to delete this user's purchases.") + return False, _("You don't have the right to delete this user's " + "purchases.") if self.facture.control or not self.facture.valid: - return False, _("You don't have the right to delete a purchase already controlled or invalidated.") + return False, _("You don't have the right to delete a purchase " + "already controlled or invalidated.") else: return True, None def can_view(self, user_request, *args, **kwargs): - if not user_request.has_perm('cotisations.view_vente') and\ - self.facture.user != user_request: - return False, _("You don't have the right to see someone else's purchase history.") + if not user_request.has_perm('cotisations.view_vente') and \ + self.facture.user != user_request: + return False, _("You don't have the right to see someone " + "else's purchase history.") else: return True, None @@ -418,12 +443,14 @@ def vente_post_delete(sender, **kwargs): class Article(RevMixin, AclMixin, models.Model): """ - The definition of an article model. It represents an type of object that can be sold to the user. - + The definition of an article model. It represents a type of object + that can be sold to the user. + It's represented by: * a name * a price - * a cotisation type (indicating if this article reprensents a cotisation or not) + * a cotisation type (indicating if this article reprensents a + cotisation or not) * a duration (if it is a cotisation) * a type of user (indicating what kind of user can buy this article) """ @@ -513,8 +540,8 @@ class Banque(RevMixin, AclMixin, models.Model): permissions = ( ('view_banque', _l("Can see a bank's details")), ) - verbose_name=_l("Bank") - verbose_name_plural=_l("Banks") + verbose_name = _l("Bank") + verbose_name_plural = _l("Banks") def __str__(self): return self.name @@ -530,7 +557,7 @@ class Paiement(RevMixin, AclMixin, models.Model): * a type (used for the type 'cheque' which implies the use of a bank and an account number in related models) """ - + PAYMENT_TYPES = ( (0, _l("Standard")), (1, _l("Cheque")), @@ -622,24 +649,29 @@ class Cotisation(RevMixin, AclMixin, models.Model): def can_edit(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.change_cotisation'): return False, _("You don't have the right to edit a cotisation.") - elif not user_request.has_perm('cotisations.change_all_cotisation') and\ - (self.vente.facture.control or not self.vente.facture.valid): - return False, _("You don't have the right to edit a cotisation already controlled or invalidated.") + elif not user_request.has_perm('cotisations.change_all_cotisation') \ + and (self.vente.facture.control or + not self.vente.facture.valid): + return False, _("You don't have the right to edit a cotisation " + "already controlled or invalidated.") else: return True, None def can_delete(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.delete_cotisation'): - return False, _("You don't have the right to delete a cotisation.") + return False, _("You don't have the right to delete a " + "cotisation.") if self.vente.facture.control or not self.vente.facture.valid: - return False, _("You don't have the right to delete a cotisation already controlled or invalidated.") + return False, _("You don't have the right to delete a cotisation " + "already controlled or invalidated.") else: return True, None def can_view(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.view_cotisation') and\ - self.vente.facture.user != user_request: - return False, _("You don't have the right to see someone else's cotisation history.") + self.vente.facture.user != user_request: + return False, _("You don't have the right to see someone else's " + "cotisation history.") else: return True, None diff --git a/cotisations/payment.py b/cotisations/payment.py index 652037fb..8efdd344 100644 --- a/cotisations/payment.py +++ b/cotisations/payment.py @@ -17,6 +17,7 @@ from preferences.models import AssoOption from .models import Facture from .payment_utils.comnpay import Payment as ComnpayPayment + @csrf_exempt @login_required def accept_payment(request, factureid): @@ -30,7 +31,10 @@ def accept_payment(request, factureid): 'amount': facture.prix() } ) - return redirect(reverse('users:profil', kwargs={'userid':request.user.id})) + return redirect(reverse( + 'users:profil', + kwargs={'userid': request.user.id} + )) @csrf_exempt @@ -43,7 +47,11 @@ def refuse_payment(request): request, _("The payment has been refused.") ) - return redirect(reverse('users:profil', kwargs={'userid':request.user.id})) + return redirect(reverse( + 'users:profil', + kwargs={'userid': request.user.id} + )) + @csrf_exempt def ipn(request): @@ -105,7 +113,7 @@ def comnpay(facture, request): str(AssoOption.get_cached_value('payment_pass')), 'https://' + host + reverse( 'cotisations:accept_payment', - kwargs={'factureid':facture.id} + kwargs={'factureid': facture.id} ), 'https://' + host + reverse('cotisations:refuse_payment'), 'https://' + host + reverse('cotisations:ipn'), @@ -113,20 +121,20 @@ def comnpay(facture, request): "D" ) r = { - 'action' : 'https://secure.homologation.comnpay.com', - 'method' : 'POST', - 'content' : p.buildSecretHTML( + 'action': 'https://secure.homologation.comnpay.com', + 'method': 'POST', + 'content': p.buildSecretHTML( "Rechargement du solde", facture.prix(), idTransaction=str(facture.id) ), - 'amount' : facture.prix, + 'amount': facture.prix, } return r # The payment systems supported by re2o PAYMENT_SYSTEM = { - 'COMNPAY' : comnpay, - 'NONE' : None + 'COMNPAY': comnpay, + 'NONE': None } diff --git a/cotisations/payment_utils/comnpay.py b/cotisations/payment_utils/comnpay.py index 6c1701d3..dcf7e22a 100644 --- a/cotisations/payment_utils/comnpay.py +++ b/cotisations/payment_utils/comnpay.py @@ -5,6 +5,7 @@ import hashlib from collections import OrderedDict from itertools import chain + class Payment(): vad_number = "" @@ -15,7 +16,8 @@ class Payment(): source = "" typeTr = "D" - def __init__(self, vad_number = "", secret_key = "", urlRetourOK = "", urlRetourNOK = "", urlIPN = "", source="", typeTr="D"): + def __init__(self, vad_number="", secret_key="", urlRetourOK="", + urlRetourNOK="", urlIPN="", source="", typeTr="D"): self.vad_number = vad_number self.secret_key = secret_key self.urlRetourOK = urlRetourOK @@ -24,36 +26,45 @@ class Payment(): self.source = source self.typeTr = typeTr - def buildSecretHTML(self, produit="Produit", montant="0.00", idTransaction=""): + def buildSecretHTML(self, produit="Produit", montant="0.00", + idTransaction=""): if idTransaction == "": - self.idTransaction = str(time.time())+self.vad_number+str(randrange(999)) + self.idTransaction = str(time.time()) + self.idTransaction += self.vad_number + self.idTransaction += str(randrange(999)) else: self.idTransaction = idTransaction - array_tpe = OrderedDict( - montant= str(montant), - idTPE= self.vad_number, - idTransaction= self.idTransaction, - devise= "EUR", - lang= 'fr', - nom_produit= produit, - source= self.source, - urlRetourOK= self.urlRetourOK, - urlRetourNOK= self.urlRetourNOK, - typeTr= str(self.typeTr) + array_tpe = OrderedDict( + montant=str(montant), + idTPE=self.vad_number, + idTransaction=self.idTransaction, + devise="EUR", + lang='fr', + nom_produit=produit, + source=self.source, + urlRetourOK=self.urlRetourOK, + urlRetourNOK=self.urlRetourNOK, + typeTr=str(self.typeTr) ) - if self.urlIPN!="": + if self.urlIPN != "": array_tpe['urlIPN'] = self.urlIPN - array_tpe['key'] = self.secret_key; - strWithKey = base64.b64encode(bytes('|'.join(array_tpe.values()), 'utf-8')) + array_tpe['key'] = self.secret_key + strWithKey = base64.b64encode(bytes( + '|'.join(array_tpe.values()), + 'utf-8' + )) del array_tpe["key"] array_tpe['sec'] = hashlib.sha512(strWithKey).hexdigest() ret = "" for key in array_tpe: - ret += '' + ret += ''.format( + 'k'=key, + 'v'=array_type[key] + ) return ret @@ -61,8 +72,10 @@ class Payment(): if "sec" in values: sec = values['sec'] del values["sec"] - strWithKey = hashlib.sha512(base64.b64encode(bytes('|'.join(values.values()) +"|"+secret_key, 'utf-8'))).hexdigest() + strWithKey = hashlib.sha512(base64.b64encode(bytes( + '|'.join(values.values()) + "|" + secret_key, + 'utf-8' + ))).hexdigest() return strWithKey.upper() == sec.upper() else: return False - diff --git a/cotisations/tex.py b/cotisations/tex.py index fb50d05b..95f12b93 100644 --- a/cotisations/tex.py +++ b/cotisations/tex.py @@ -46,7 +46,7 @@ def render_invoice(request, ctx={}): date, the user, the articles, the prices, ... """ filename = '_'.join([ - 'invoice', + 'invoice', slugify(ctx['asso_name']), slugify(ctx['recipient_name']), str(ctx['DATE'].year), @@ -54,9 +54,12 @@ def render_invoice(request, ctx={}): str(ctx['DATE'].day), ]) r = render_tex(request, 'cotisations/factures.tex', ctx) - r['Content-Disposition'] = ''.join(['attachment; filename="',filename,'.pdf"']) + r['Content-Disposition'] = 'attachment; filename="{name}.pdf"'.format( + name=filename + ) return r + def render_tex(request, template, ctx={}): """ Creates a PDF from a LaTex templates using pdflatex. @@ -66,13 +69,13 @@ def render_tex(request, template, ctx={}): context = Context(ctx) template = get_template(template) rendered_tpl = template.render(context).encode('utf-8') - + with tempfile.TemporaryDirectory() as tempdir: for i in range(2): process = Popen( ['pdflatex', '-output-directory', tempdir], - stdin = PIPE, - stdout = PIPE, + stdin=PIPE, + stdout=PIPE, ) process.communicate(rendered_tpl) with open(os.path.join(tempdir, 'texput.pdf'), 'rb') as f: diff --git a/cotisations/urls.py b/cotisations/urls.py index 0040e48c..05b1a7a3 100644 --- a/cotisations/urls.py +++ b/cotisations/urls.py @@ -101,12 +101,11 @@ urlpatterns = [ views.index_paiement, name='index-paiement' ), - url( - r'history/(?P\w+)/(?P[0-9]+)$', + url(r'history/(?P\w+)/(?P[0-9]+)$', re2o.views.history, name='history', - kwargs={'application':'cotisations'}, - ), + kwargs={'application': 'cotisations'}, + ), url(r'^control/$', views.control, name='control' @@ -122,14 +121,14 @@ urlpatterns = [ url(r'^payment/accept/(?P[0-9]+)$', payment.accept_payment, name='accept_payment' - ), + ), url(r'^payment/refuse/$', payment.refuse_payment, name='refuse_payment' - ), + ), url(r'^payment/ipn/$', payment.ipn, name='ipn' - ), + ), url(r'^$', views.index, name='index'), ] diff --git a/cotisations/views.py b/cotisations/views.py index 631713e8..c8c68c19 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -77,7 +77,6 @@ from . import payment as online_payment from .tex import render_invoice - @login_required @can_create(Facture) @can_edit(User) @@ -102,9 +101,13 @@ def new_facture(request, user, userid): # Building the invocie form and the article formset invoice_form = NewFactureForm(request.POST or None, instance=invoice) if request.user.is_class_club: - article_formset = formset_factory(SelectClubArticleForm)(request.POST or None) + article_formset = formset_factory(SelectClubArticleForm)( + request.POST or None + ) else: - article_formset = formset_factory(SelectUserArticleForm)(request.POST or None) + article_formset = formset_factory(SelectUserArticleForm)( + request.POST or None + ) if invoice_form.is_valid() and article_formset.is_valid(): new_invoice_instance = invoice_form.save(commit=False) @@ -118,15 +121,18 @@ def new_facture(request, user, userid): # the authorized minimum (negative_balance) if user_balance: # TODO : change Paiement to Payment - if new_invoice_instance.paiement == Paiement.objects.get_or_create( - moyen='solde' - )[0]: + if new_invoice_instance.paiement == ( + Paiement.objects.get_or_create(moyen='solde')[0] + ): total_price = 0 for art_item in articles: if art_item.cleaned_data: - total_price += art_item.cleaned_data['article']\ - .prix*art_item.cleaned_data['quantity'] - if float(user.solde) - float(total_price) < negative_balance: + total_price += ( + art_item.cleaned_data['article'].prix * + art_item.cleaned_data['quantity'] + ) + if (float(user.solde) - float(total_price) + < negative_balance): messages.error( request, _("Your balance is too low for this operation.") @@ -194,7 +200,7 @@ def new_facture(request, user, userid): @can_change(Facture, 'pdf') def new_facture_pdf(request): """ - View used to generate a custom PDF invoice. It's mainly used to + View used to generate a custom PDF invoice. It's mainly used to get invoices that are not taken into account, for the administrative point of view. """ @@ -205,9 +211,13 @@ def new_facture_pdf(request): # Building the invocie form and the article formset invoice_form = NewFactureFormPdf(request.POST or None) if request.user.is_class_club: - articles_formset = formset_factory(SelectClubArticleForm)(request.POST or None) + articles_formset = formset_factory(SelectClubArticleForm)( + request.POST or None + ) else: - articles_formset = formset_factory(SelectUserArticleForm)(request.POST or None) + articles_formset = formset_factory(SelectUserArticleForm)( + request.POST or None + ) if invoice_form.is_valid() and articles_formset.is_valid(): # Get the article list and build an list out of it # contiaining (article_name, article_price, quantity, total_price) @@ -303,7 +313,11 @@ def edit_facture(request, facture, factureid): can be set as desired. This is also the view used to invalidate an invoice. """ - invoice_form = EditFactureForm(request.POST or None, instance=facture, user=request.user) + invoice_form = EditFactureForm( + request.POST or None, + instance=facture, + user=request.user + ) purchases_objects = Vente.objects.filter(facture=facture) purchase_form_set = modelformset_factory( Vente, @@ -311,7 +325,10 @@ def edit_facture(request, facture, factureid): extra=0, max_num=len(purchases_objects) ) - purchase_form = purchase_form_set(request.POST or None, queryset=purchases_objects) + purchase_form = purchase_form_set( + request.POST or None, + queryset=purchases_objects + ) if invoice_form.is_valid() and purchase_form.is_valid(): if invoice_form.changed_data: invoice_form.save() @@ -385,7 +402,7 @@ def credit_solde(request, user, userid): def add_article(request): """ View used to add an article. - + .. note:: If a purchase has already been sold, the price are calculated once and for all. That means even if the price of an article is edited later, it won't change the invoice. That is really important to keep @@ -613,7 +630,8 @@ def control(request): View used to control the invoices all at once. """ pagination_number = GeneralOption.get_cached_value('pagination_number') - invoice_list = Facture.objects.select_related('user').select_related('paiement') + invoice_list = (Facture.objects.select_related('user'). + select_related('paiement')) invoice_list = SortTable.sort( invoice_list, request.GET.get('col'), @@ -725,9 +743,13 @@ def new_facture_solde(request, userid): Q(type_user='All') | Q(type_user=request.user.class_name) ) if request.user.is_class_club: - article_formset = formset_factory(SelectClubArticleForm)(request.POST or None) + article_formset = formset_factory(SelectClubArticleForm)( + request.POST or None + ) else: - article_formset = formset_factory(SelectUserArticleForm)(request.POST or None) + article_formset = formset_factory(SelectUserArticleForm)( + request.POST or None + ) if article_formset.is_valid(): articles = article_formset @@ -826,7 +848,9 @@ def recharge(request): refill_form = RechargeForm(request.POST or None, user=request.user) if refill_form.is_valid(): invoice = Facture(user=request.user) - payment, _created = Paiement.objects.get_or_create(moyen='Rechargement en ligne') + payment, _created = Paiement.objects.get_or_create( + moyen='Rechargement en ligne' + ) invoice.paiement = payment invoice.valid = False invoice.save() @@ -837,7 +861,9 @@ def recharge(request): number=1 ) purchase.save() - content = online_payment.PAYMENT_SYSTEM[AssoOption.get_cached_value('payment')](invoice, request) + content = online_payment.PAYMENT_SYSTEM[ + AssoOption.get_cached_value('payment') + ](invoice, request) return render(request, 'cotisations/payment.html', content) return form({ 'rechargeform': refill_form From 0dace45c5e926587af1857dd43fee8c21da41b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Fri, 13 Apr 2018 19:35:52 +0000 Subject: [PATCH 02/22] Pep8 compliance on api --- api/serializers.py | 30 ++++---- api/urls.py | 15 +++- api/utils.py | 41 +++++----- api/views.py | 183 ++++++++++++++++++++++++++++++++++----------- 4 files changed, 191 insertions(+), 78 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index 1b02c577..8e4c49aa 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -203,7 +203,7 @@ class ExtensionSerializer(serializers.ModelSerializer): return str(obj.dns_entry) def get_soa_data(self, obj): - return { 'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param } + return {'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param} class MxSerializer(serializers.ModelSerializer): @@ -338,23 +338,23 @@ class OuverturePortsSerializer(serializers.Serializer): ipv6 = serializers.SerializerMethodField() def get_ipv4(): - return {i.ipv4.ipv4: - { - "tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()], - "tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()], - "udp_in":[j.udp_ports_in() for j in i.port_lists.all()], - "udp_out":[j.udp_ports_out() for j in i.port_lists.all()], + return { + i.ipv4.ipv4: { + "tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()], + "tcp_out": [j.tcp_ports_out()for j in i.port_lists.all()], + "udp_in": [j.udp_ports_in() for j in i.port_lists.all()], + "udp_out": [j.udp_ports_out() for j in i.port_lists.all()], } - for i in Interface.objects.all() if i.ipv4 + for i in Interface.objects.all() if i.ipv4 } def get_ipv6(): - return {i.ipv6: - { - "tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()], - "tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()], - "udp_in":[j.udp_ports_in() for j in i.port_lists.all()], - "udp_out":[j.udp_ports_out() for j in i.port_lists.all()], + return { + i.ipv6: { + "tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()], + "tcp_out": [j.tcp_ports_out()for j in i.port_lists.all()], + "udp_in": [j.udp_ports_in() for j in i.port_lists.all()], + "udp_out": [j.udp_ports_out() for j in i.port_lists.all()], } - for i in Interface.objects.all() if i.ipv6 + for i in Interface.objects.all() if i.ipv6 } diff --git a/api/urls.py b/api/urls.py index f9838d38..9b77f308 100644 --- a/api/urls.py +++ b/api/urls.py @@ -32,7 +32,10 @@ from . import views urlpatterns = [ # Services url(r'^services/$', views.services), - url(r'^services/(?P\w+)/(?P\w+)/regen/$', views.services_server_service_regen), + url( + r'^services/(?P\w+)/(?P\w+)/regen/$', + views.services_server_service_regen + ), url(r'^services/(?P\w+)/$', views.services_server), # DNS @@ -56,7 +59,13 @@ urlpatterns = [ # Mailings url(r'^mailing/standard/$', views.mailing_standard), - url(r'^mailing/standard/(?P\w+)/members/$', views.mailing_standard_ml_members), + url( + r'^mailing/standard/(?P\w+)/members/$', + views.mailing_standard_ml_members + ), url(r'^mailing/club/$', views.mailing_club), - url(r'^mailing/club/(?P\w+)/members/$', views.mailing_club_ml_members), + url( + r'^mailing/club/(?P\w+)/members/$', + views.mailing_club_ml_members + ), ] diff --git a/api/utils.py b/api/utils.py index 024c9a39..79aba0a1 100644 --- a/api/utils.py +++ b/api/utils.py @@ -26,6 +26,7 @@ Set of various and usefull functions for the API app from rest_framework.renderers import JSONRenderer from django.http import HttpResponse + class JSONResponse(HttpResponse): """A JSON response that can be send as an HTTP response. Usefull in case of REST API. @@ -51,23 +52,23 @@ class JSONResponse(HttpResponse): class JSONError(JSONResponse): """A JSON response when the request failed. """ - + def __init__(self, error_msg, data=None, **kwargs): """Initialise a JSONError object. - + Args: error_msg: A message explaining where the error is. data: An optional field for further data to send along. - + Creates: - A JSONResponse containing a field `status` set to `error` and a field - `reason` containing `error_msg`. If `data` argument has been given, - a field `data` containing it is added to the JSON response. + A JSONResponse containing a field `status` set to `error` and a + field `reason` containing `error_msg`. If `data` argument has been + given, a field `data` containing it is added to the JSON response. """ - + response = { - 'status' : 'error', - 'reason' : error_msg + 'status': 'error', + 'reason': error_msg } if data is not None: response['data'] = data @@ -77,22 +78,22 @@ class JSONError(JSONResponse): class JSONSuccess(JSONResponse): """A JSON response when the request suceeded. """ - + def __init__(self, data=None, **kwargs): """Initialise a JSONSucess object. - + Args: error_msg: A message explaining where the error is. data: An optional field for further data to send along. - + Creates: - A JSONResponse containing a field `status` set to `sucess`. If `data` - argument has been given, a field `data` containing it is added to the - JSON response. + A JSONResponse containing a field `status` set to `sucess`. If + `data` argument has been given, a field `data` containing it is + added to the JSON response. """ - + response = { - 'status' : 'success', + 'status': 'success', } if data is not None: response['data'] = data @@ -103,12 +104,16 @@ def accept_method(methods): """Decorator to set a list of accepted request method. Check if the method used is accepted. If not, send a NotAllowed response. """ + def decorator(view): def wrapper(request, *args, **kwargs): if request.method in methods: return view(request, *args, **kwargs) else: - return JSONError('Invalid request method. Request methods authorize are '+str(methods)) + return JSONError( + 'Invalid request method. Request methods authorize are ' + + str(methods) + ) return view(request, *args, **kwargs) return wrapper return decorator diff --git a/api/views.py b/api/views.py index 3e4b90ed..47a34baf 100644 --- a/api/views.py +++ b/api/views.py @@ -30,8 +30,13 @@ from django.views.decorators.csrf import csrf_exempt from re2o.utils import all_has_access, all_active_assigned_interfaces from users.models import Club -from machines.models import (Service_link, Service, Interface, Domain, - OuverturePortList) +from machines.models import ( + Service_link, + Service, + Interface, + Domain, + OuverturePortList +) from .serializers import * from .utils import JSONError, JSONSuccess, accept_method @@ -47,15 +52,20 @@ def services(request): Return: GET: A JSONSuccess response with a field `data` containing: - * a list of dictionnaries (one for each service-server couple) containing: + * a list of dictionnaries (one for each service-server couple) + containing: * a field `server`: the server name * a field `service`: the service name * a field `need_regen`: does the service need a regeneration ? """ - service_link = Service_link.objects.all().select_related('server__domain').select_related('service') + + service_link = (Service_link.objects.all() + .select_related('server__domain') + .select_related('service')) seria = ServicesSerializer(service_link, many=True) return JSONSuccess(seria.data) + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -72,6 +82,7 @@ def services_server_service_regen(request, server_name, service_name): POST: An empty JSONSuccess response. """ + query = Service_link.objects.filter( service__in=Service.objects.filter(service_type=service_name), server__in=Interface.objects.filter( @@ -80,7 +91,7 @@ def services_server_service_regen(request, server_name, service_name): ) if not query: return JSONError("This service is not active for this server") - + service = query.first() if request.method == 'GET': return JSONSuccess({'need_regen': service.need_regen()}) @@ -102,6 +113,7 @@ def services_server(request, server_name): * a list of dictionnaries (one for each service) containing: * a field `name`: the name of a service """ + query = Service_link.objects.filter( server__in=Interface.objects.filter( domain__in=Domain.objects.filter(name=server_name) @@ -109,7 +121,7 @@ def services_server(request, server_name): ) if not query: return JSONError("This service is not active for this server") - + services = query.all() seria = ServiceLinkSerializer(services, many=True) return JSONSuccess(seria.data) @@ -135,8 +147,10 @@ def dns_mac_ip_dns(request): * a field `ip_type`: the name of the IpType of this interface * a field `mac_address`: the MAC of this interface * a field `domain`: the DNS name for this interface - * a field `extension`: the extension for the DNS zone of this interface + * a field `extension`: the extension for the DNS zone of this + interface """ + interfaces = all_active_assigned_interfaces(full=True) seria = FullInterfaceSerializer(interfaces, many=True) return JSONSuccess(seria.data) @@ -154,11 +168,23 @@ def dns_alias(request): A JSON Success response with a field `data` containing: * a list of dictionnaries (one for each alias) containing: * a field `name`: the alias used - * a field `cname`: the target of the alias (real name of the interface) - * a field `cname_entry`: the entry to write in the DNS to have the alias - * a field `extension`: the extension for the DNS zone of this interface + * a field `cname`: the target of the alias (real name of the + interface) + * a field `cname_entry`: the entry to write in the DNS to have + the alias + * a field `extension`: the extension for the DNS zone of this + interface """ - alias = Domain.objects.filter(interface_parent=None).filter(cname__in=Domain.objects.filter(interface_parent__in=Interface.objects.exclude(ipv4=None))).select_related('extension').select_related('cname__extension') + + alias = (Domain.objects + .filter(interface_parent=None) + .filter( + cname__in=Domain.objects.filter( + interface_parent__in=Interface.objects.exclude(ipv4=None) + ) + ) + .select_related('extension') + .select_related('cname__extension')) seria = DomainSerializer(alias, many=True) return JSONSuccess(seria.data) @@ -185,10 +211,12 @@ def accesspoint_ip_dns(request): * a field `ip_type`: the name of the IpType of this interface * a field `mac_address`: the MAC of this interface * a field `domain`: the DNS name for this interface - * a field `extension`: the extension for the DNS zone of this interface + * a field `extension`: the extension for the DNS zone of this + interface """ - interfaces = all_active_assigned_interfaces(full=True)\ - .filter(machine__accesspoint__isnull=False) + + interfaces = (all_active_assigned_interfaces(full=True) + .filter(machine__accesspoint__isnull=False)) seria = FullInterfaceSerializer(interfaces, many=True) return JSONSuccess(seria.data) @@ -208,12 +236,14 @@ def dns_corresp(request): * a field `extension`: the DNS extension associated * a field `domain_ip_start`: the first ip to use for this type * a field `domain_ip_stop`: the last ip to use for this type - * a field `prefix_v6`: `null` if IPv6 is deactivated else the prefix to use + * a field `prefix_v6`: `null` if IPv6 is deactivated else the + prefix to use * a field `ouverture_ports_tcp_in`: the policy for TCP IN ports * a field `ouverture_ports_tcp_out`: the policy for TCP OUT ports * a field `ouverture_ports_udp_in`: the policy for UDP IN ports * a field `ouverture_ports_udp_out`: the policy for UDP OUT ports """ + ip_type = IpType.objects.all().select_related('extension') seria = TypeSerializer(ip_type, many=True) return JSONSuccess(seria.data) @@ -233,9 +263,13 @@ def dns_mx(request): * a field `zone`: the extension for the concerned zone * a field `priority`: the priority to use * a field `name`: the name of the target - * a field `mx_entry`: the full entry to add in the DNS for this MX record + * a field `mx_entry`: the full entry to add in the DNS for this + MX record """ - mx = Mx.objects.all().select_related('zone').select_related('name__extension') + + mx = (Mx.objects.all() + .select_related('zone') + .select_related('name__extension')) seria = MxSerializer(mx, many=True) return JSONSuccess(seria.data) @@ -253,9 +287,18 @@ def dns_ns(request): * a list of dictionnaries (one for each NS record) containing: * a field `zone`: the extension for the concerned zone * a field `ns`: the DNS name for the NS server targeted - * a field `ns_entry`: the full entry to add in the DNS for this NS record + * a field `ns_entry`: the full entry to add in the DNS for this + NS record """ - ns = Ns.objects.exclude(ns__in=Domain.objects.filter(interface_parent__in=Interface.objects.filter(ipv4=None))).select_related('zone').select_related('ns__extension') + + ns = (Ns.objects + .exclude( + ns__in=Domain.objects.filter( + interface_parent__in=Interface.objects.filter(ipv4=None) + ) + ) + .select_related('zone') + .select_related('ns__extension')) seria = NsSerializer(ns, many=True) return JSONSuccess(seria.data) @@ -274,8 +317,10 @@ def dns_txt(request): * a field `zone`: the extension for the concerned zone * a field `field1`: the first field in the record (target) * a field `field2`: the second field in the record (value) - * a field `txt_entry`: the full entry to add in the DNS for this TXT record + * a field `txt_entry`: the full entry to add in the DNS for this + TXT record """ + txt = Txt.objects.all().select_related('zone') seria = TxtSerializer(txt, many=True) return JSONSuccess(seria.data) @@ -300,9 +345,13 @@ def dns_srv(request): * a field `weight`: the weight for same priority entries * a field `port`: the port targeted * a field `target`: the interface targeted by this service - * a field `srv_entry`: the full entry to add in the DNS for this SRV record + * a field `srv_entry`: the full entry to add in the DNS for this + SRV record """ - srv = Srv.objects.all().select_related('extension').select_related('target__extension') + + srv = (Srv.objects.all() + .select_related('extension') + .select_related('target__extension')) seria = SrvSerializer(srv, many=True) return JSONSuccess(seria.data) @@ -312,7 +361,7 @@ def dns_srv(request): @permission_required('machines.serveur') @accept_method(['GET']) def dns_zones(request): - """The list of the zones managed + """The list of the zones managed Returns: GET: @@ -320,16 +369,22 @@ def dns_zones(request): * a list of dictionnaries (one for each zone) containing: * a field `name`: the extension for the zone * a field `origin`: the server IPv4 for the orgin of the zone - * a field `origin_v6`: `null` if ipv6 is deactivated else the server IPv6 for the origin of the zone + * a field `origin_v6`: `null` if ipv6 is deactivated else the + server IPv6 for the origin of the zone * a field `soa` containing: - * a field `mail` containing the mail to contact in case of problem with the zone - * a field `param` containing the full soa paramters to use in the DNS for this zone - * a field `zone_entry`: the full entry to add in the DNS for the origin of the zone + * a field `mail` containing the mail to contact in case of + problem with the zone + * a field `param` containing the full soa paramters to use + in the DNS for this zone + * a field `zone_entry`: the full entry to add in the DNS for the + origin of the zone """ + zones = Extension.objects.all().select_related('origin') seria = ExtensionSerializer(zones, many=True) return JSONSuccess(seria.data) + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -359,32 +414,68 @@ def firewall_ouverture_ports(request): * a field `udp_out` containing: * a list of port number where ipv6 udp out should be ok """ - r = {'ipv4':{}, 'ipv6':{}} - for o in OuverturePortList.objects.all().prefetch_related('ouvertureport_set').prefetch_related('interface_set', 'interface_set__ipv4'): + + r = {'ipv4': {}, 'ipv6': {}} + for o in (OuverturePortList.objects.all() + .prefetch_related('ouvertureport_set') + .prefetch_related('interface_set', 'interface_set__ipv4')): pl = { - "tcp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.IN))), - "tcp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.OUT))), - "udp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.IN))), - "udp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.OUT))), + "tcp_in": set(map( + str, + o.ouvertureport_set.filter( + protocole=OuverturePort.TCP, + io=OuverturePort.IN + ) + )), + "tcp_out": set(map( + str, + o.ouvertureport_set.filter( + protocole=OuverturePort.TCP, + io=OuverturePort.OUT + ) + )), + "udp_in": set(map( + str, + o.ouvertureport_set.filter( + protocole=OuverturePort.UDP, + io=OuverturePort.IN + ) + )), + "udp_out": set(map( + str, + o.ouvertureport_set.filter( + protocole=OuverturePort.UDP, + io=OuverturePort.OUT + ) + )), } for i in filter_active_interfaces(o.interface_set): if i.may_have_port_open(): d = r['ipv4'].get(i.ipv4.ipv4, {}) - d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"]) - d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"]) - d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"]) - d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"]) + d["tcp_in"] = (d.get("tcp_in", set()) + .union(pl["tcp_in"])) + d["tcp_out"] = (d.get("tcp_out", set()) + .union(pl["tcp_out"])) + d["udp_in"] = (d.get("udp_in", set()) + .union(pl["udp_in"])) + d["udp_out"] = (d.get("udp_out", set()) + .union(pl["udp_out"])) r['ipv4'][i.ipv4.ipv4] = d if i.ipv6(): for ipv6 in i.ipv6(): d = r['ipv6'].get(ipv6.ipv6, {}) - d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"]) - d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"]) - d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"]) - d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"]) + d["tcp_in"] = (d.get("tcp_in", set()) + .union(pl["tcp_in"])) + d["tcp_out"] = (d.get("tcp_out", set()) + .union(pl["tcp_out"])) + d["udp_in"] = (d.get("udp_in", set()) + .union(pl["udp_in"])) + d["udp_out"] = (d.get("udp_out", set()) + .union(pl["udp_out"])) r['ipv6'][ipv6.ipv6] = d return JSONSuccess(r) + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -402,8 +493,10 @@ def dhcp_mac_ip(request): * a field `ip_type`: the name of the IpType of this interface * a field `mac_address`: the MAC of this interface * a field `domain`: the DNS name for this interface - * a field `extension`: the extension for the DNS zone of this interface + * a field `extension`: the extension for the DNS zone of this + interface """ + interfaces = all_active_assigned_interfaces() seria = InterfaceSerializer(interfaces, many=True) return JSONSuccess(seria.data) @@ -422,10 +515,12 @@ def mailing_standard(request): * a list of dictionnaries (one for each mailing) containing: * a field `name`: the name of a mailing """ + return JSONSuccess([ {'name': 'adherents'} ]) + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -442,6 +537,7 @@ def mailing_standard_ml_members(request): * a field `surname`: the surname of the member * a field `pseudo`: the pseudo of the member """ + # All with active connextion if ml_name == 'adherents': members = all_has_access().values('email').distinct() @@ -465,10 +561,12 @@ def mailing_club(request): * a list of dictionnaries (one for each mailing) containing: * a field `name` indicating the name of a mailing """ + clubs = Club.objects.filter(mailing=True).values('pseudo') seria = MailingSerializer(clubs, many=True) return JSONSuccess(seria.data) + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -485,6 +583,7 @@ def mailing_club_ml_members(request): * a field `surname`: the surname of the member * a field `pseudo`: the pseudo of the member """ + try: club = Club.objects.get(mailing=True, pseudo=ml_name) except Club.DoesNotExist: From 9d7a7fa1f556252ae3c42038bf3e769a94ddc9eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Fri, 13 Apr 2018 20:11:55 +0000 Subject: [PATCH 03/22] Pep8 compliance on freeradius --- freeradius_utils/auth.py | 228 +++++++++++++++++++++++++++------------ 1 file changed, 162 insertions(+), 66 deletions(-) diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index 5d3195da..b25fa003 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -1,4 +1,4 @@ -# ⁻*- mode: python; coding: utf-8 -*- +# -*- mode: python; coding: utf-8 -*- # Re2o est un logiciel d'administration développé initiallement au rezometz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. @@ -37,11 +37,20 @@ Inspiré du travail de Daniel Stan au Crans import logging import netaddr -import radiusd # Module magique freeradius (radiusd.py is dummy) +import radiusd # Module magique freeradius (radiusd.py is dummy) import binascii import hashlib -import os, sys +import os +import sys +from django.core.wsgi import get_wsgi_application +import argparse + +from django.db.models import Q +from machines.models import Interface, IpList, Nas, Domain +from topologie.models import Room, Port, Switch +from users.models import User +from preferences.models import OptionalTopologie proj_path = "/var/www/re2o/" # This is so Django knows where to find stuff. @@ -52,28 +61,17 @@ sys.path.append(proj_path) os.chdir(proj_path) # This is so models get loaded. -from django.core.wsgi import get_wsgi_application application = get_wsgi_application() -import argparse - -from django.db.models import Q -from machines.models import Interface, IpList, Nas, Domain -from topologie.models import Room, Port, Switch -from users.models import User -from preferences.models import OptionalTopologie - options, created = OptionalTopologie.objects.get_or_create() VLAN_NOK = options.vlan_decision_nok.vlan_id VLAN_OK = options.vlan_decision_ok.vlan_id - #: Serveur radius de test (pas la prod) TEST_SERVER = bool(os.getenv('DBG_FREERADIUS', False)) -## -*- Logging -*- - +# Logging class RadiusdHandler(logging.Handler): """Handler de logs pour freeradius""" @@ -87,6 +85,7 @@ class RadiusdHandler(logging.Handler): rad_sig = radiusd.L_DBG radiusd.radlog(rad_sig, record.msg) + # Initialisation d'un logger (pour logguer unifié) logger = logging.getLogger('auth.py') logger.setLevel(logging.DEBUG) @@ -95,10 +94,11 @@ handler = RadiusdHandler() handler.setFormatter(formatter) logger.addHandler(handler) + def radius_event(fun): """Décorateur pour les fonctions d'interfaces avec radius. - Une telle fonction prend un uniquement argument, qui est une liste de tuples - (clé, valeur) et renvoie un triplet dont les composantes sont : + Une telle fonction prend un uniquement argument, qui est une liste de + tuples (clé, valeur) et renvoie un triplet dont les composantes sont : * le code de retour (voir radiusd.RLM_MODULE_* ) * un tuple de couples (clé, valeur) pour les valeurs de réponse (accès ok et autres trucs du genre) @@ -118,8 +118,8 @@ def radius_event(fun): # Ex: Calling-Station-Id: "une_adresse_mac" data[key] = value.replace('"', '') try: - # TODO s'assurer ici que les tuples renvoyés sont bien des (str,str) - # rlm_python ne digère PAS les unicodes + # TODO s'assurer ici que les tuples renvoyés sont bien des + # (str,str) : rlm_python ne digère PAS les unicodes return fun(data) except Exception as err: logger.error('Failed %r on data %r' % (err, auth_data)) @@ -128,7 +128,6 @@ def radius_event(fun): return new_f - @radius_event def instantiate(*_): """Utile pour initialiser les connexions ldap une première fois (otherwise, @@ -137,12 +136,15 @@ def instantiate(*_): if TEST_SERVER: logger.info(u'DBG_FREERADIUS is enabled') + @radius_event def authorize(data): """On test si on connait le calling nas: - - si le nas est inconnue, on suppose que c'est une requète 802.1X, on la traite + - si le nas est inconnue, on suppose que c'est une requète 802.1X, on la + traite - si le nas est connu, on applique 802.1X si le mode est activé - - si le nas est connu et si il s'agit d'un nas auth par mac, on repond accept en authorize + - si le nas est connu et si il s'agit d'un nas auth par mac, on repond + accept en authorize """ # Pour les requetes proxifiees, on split nas = data.get('NAS-IP-Address', data.get('NAS-Identifier', None)) @@ -155,28 +157,35 @@ def authorize(data): user = data.get('User-Name', '').decode('utf-8', errors='replace') user = user.split('@', 1)[0] mac = data.get('Calling-Station-Id', '') - result, log, password = check_user_machine_and_register(nas_type, user, mac) + result, log, password = check_user_machine_and_register( + nas_type, + user, + mac + ) logger.info(log.encode('utf-8')) logger.info(user.encode('utf-8')) if not result: return radiusd.RLM_MODULE_REJECT else: - return (radiusd.RLM_MODULE_UPDATED, - (), - ( - (str("NT-Password"), str(password)), - ), + return ( + radiusd.RLM_MODULE_UPDATED, + (), + ( + (str("NT-Password"), str(password)), + ), ) else: - return (radiusd.RLM_MODULE_UPDATED, - (), - ( - ("Auth-Type", "Accept"), - ), + return ( + radiusd.RLM_MODULE_UPDATED, + (), + ( + ("Auth-Type", "Accept"), + ), ) + @radius_event def post_auth(data): nas = data.get('NAS-IP-Address', data.get('NAS-Identifier', None)) @@ -187,61 +196,87 @@ def post_auth(data): return radiusd.RLM_MODULE_OK nas_type = Nas.objects.filter(nas_type=nas_instance.type).first() if not nas_type: - logger.info(u"Type de nas non enregistré dans la bdd!".encode('utf-8')) + logger.info( + u"Type de nas non enregistré dans la bdd!".encode('utf-8') + ) return radiusd.RLM_MODULE_OK mac = data.get('Calling-Station-Id', None) - # Switch et bornes héritent de machine et peuvent avoir plusieurs interfaces filles + # Switch et bornes héritent de machine et peuvent avoir plusieurs + # interfaces filles nas_machine = nas_instance.machine # Si il s'agit d'un switch if hasattr(nas_machine, 'switch'): port = data.get('NAS-Port-Id', data.get('NAS-Port', None)) - #Pour les infrastructures possédant des switchs Juniper : - #On vérifie si le switch fait partie d'un stack Juniper + # Pour les infrastructures possédant des switchs Juniper : + # On vérifie si le switch fait partie d'un stack Juniper instance_stack = nas_machine.switch.stack if instance_stack: # Si c'est le cas, on resélectionne le bon switch dans la stack id_stack_member = port.split("-")[1].split('/')[0] - nas_machine = Switch.objects.filter(stack=instance_stack).filter(stack_member_id=id_stack_member).prefetch_related('interface_set__domain__extension').first() - # On récupère le numéro du port sur l'output de freeradius. La ligne suivante fonctionne pour cisco, HP et Juniper + nas_machine = (Switch.objects + .filter(stack=instance_stack) + .filter(stack_member_id=id_stack_member) + .prefetch_related( + 'interface_set__domain__extension' + ) + .first()) + # On récupère le numéro du port sur l'output de freeradius. + # La ligne suivante fonctionne pour cisco, HP et Juniper port = port.split(".")[0].split('/')[-1][-2:] out = decide_vlan_and_register_switch(nas_machine, nas_type, port, mac) sw_name, room, reason, vlan_id = out - log_message = '(fil) %s -> %s [%s%s]' % \ - (sw_name + u":" + port + u"/" + unicode(room), mac, vlan_id, (reason and u': ' + reason).encode('utf-8')) + log_message = '(fil) %s -> %s [%s%s]' % ( + sw_name + u":" + port + u"/" + unicode(room), + mac, + vlan_id, + (reason and u': ' + reason).encode('utf-8') + ) logger.info(log_message) # Filaire - return (radiusd.RLM_MODULE_UPDATED, + return ( + radiusd.RLM_MODULE_UPDATED, ( ("Tunnel-Type", "VLAN"), ("Tunnel-Medium-Type", "IEEE-802"), ("Tunnel-Private-Group-Id", '%d' % int(vlan_id)), ), () - ) + ) else: return radiusd.RLM_MODULE_OK + @radius_event def dummy_fun(_): """Do nothing, successfully. (C'est pour avoir un truc à mettre)""" return radiusd.RLM_MODULE_OK + def detach(_=None): """Appelé lors du déchargement du module (enfin, normalement)""" print "*** goodbye from auth.py ***" return radiusd.RLM_MODULE_OK + def find_nas_from_request(nas_id): - nas = Interface.objects.filter(Q(domain=Domain.objects.filter(name=nas_id)) | Q(ipv4=IpList.objects.filter(ipv4=nas_id))).select_related('type').select_related('machine__switch__stack') + nas = (Interface.objects + .filter( + Q(domain=Domain.objects.filter(name=nas_id)) | + Q(ipv4=IpList.objects.filter(ipv4=nas_id)) + ) + .select_related('type') + .select_related('machine__switch__stack')) return nas.first() + def check_user_machine_and_register(nas_type, username, mac_address): - """ Verifie le username et la mac renseignee. L'enregistre si elle est inconnue. + """Verifie le username et la mac renseignee. L'enregistre si elle est + inconnue. Renvoie le mot de passe ntlm de l'user si tout est ok Utilise pour les authentifications en 802.1X""" interface = Interface.objects.filter(mac_address=mac_address).first() @@ -252,7 +287,10 @@ def check_user_machine_and_register(nas_type, username, mac_address): return (False, u"Adhérent non cotisant", '') if interface: if interface.machine.user != user: - return (False, u"Machine enregistrée sur le compte d'un autre user...", '') + return (False, + u"Machine enregistrée sur le compte d'un autre " + "user...", + '') elif not interface.is_active: return (False, u"Machine desactivée", '') elif not interface.ipv4: @@ -264,7 +302,9 @@ def check_user_machine_and_register(nas_type, username, mac_address): if nas_type.autocapture_mac: result, reason = user.autoregister_machine(mac_address, nas_type) if result: - return (True, u'Access Ok, Capture de la mac...', user.pwd_ntlm) + return (True, + u'Access Ok, Capture de la mac...', + user.pwd_ntlm) else: return (False, u'Erreur dans le register mac %s' % reason, '') else: @@ -273,8 +313,10 @@ def check_user_machine_and_register(nas_type, username, mac_address): return (False, u"Machine inconnue", '') -def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_address): - """Fonction de placement vlan pour un switch en radius filaire auth par mac. +def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, + mac_address): + """Fonction de placement vlan pour un switch en radius filaire auth par + mac. Plusieurs modes : - nas inconnu, port inconnu : on place sur le vlan par defaut VLAN_OK - pas de radius sur le port : VLAN_OK @@ -292,7 +334,8 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_addr - interface inconnue : - register mac désactivé : VLAN_NOK - register mac activé : - - dans la chambre associé au port, pas d'user ou non à jour : VLAN_NOK + - dans la chambre associé au port, pas d'user ou non à + jour : VLAN_NOK - user à jour, autocapture de la mac et VLAN_OK """ # Get port from switch and port number @@ -303,8 +346,13 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_addr sw_name = str(nas_machine) - port = Port.objects.filter(switch=Switch.objects.filter(machine_ptr=nas_machine), port=port_number).first() - #Si le port est inconnu, on place sur le vlan defaut + port = (Port.objects + .filter( + switch=Switch.objects.filter(machine_ptr=nas_machine), + port=port_number + ) + .first()) + # Si le port est inconnu, on place sur le vlan defaut if not port: return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK) @@ -316,7 +364,10 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_addr DECISION_VLAN = VLAN_OK if port.radius == 'NO': - return (sw_name, "", u"Pas d'authentification sur ce port" + extra_log, DECISION_VLAN) + return (sw_name, + "", + u"Pas d'authentification sur ce port" + extra_log, + DECISION_VLAN) if port.radius == 'BLOQ': return (sw_name, port.room, u'Port desactive', VLAN_NOK) @@ -326,7 +377,9 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_addr if not room: return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK) - room_user = User.objects.filter(Q(club__room=port.room) | Q(adherent__room=port.room)) + room_user = User.objects.filter( + Q(club__room=port.room) | Q(adherent__room=port.room) + ) if not room_user: return (sw_name, room, u'Chambre non cotisante', VLAN_NOK) for user in room_user: @@ -336,35 +389,78 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_addr if port.radius == 'COMMON' or port.radius == 'STRICT': # Authentification par mac - interface = Interface.objects.filter(mac_address=mac_address).select_related('machine__user').select_related('ipv4').first() + interface = (Interface.objects + .filter(mac_address=mac_address) + .select_related('machine__user') + .select_related('ipv4') + .first()) if not interface: room = port.room # On essaye de register la mac if not nas_type.autocapture_mac: return (sw_name, "", u'Machine inconnue', VLAN_NOK) elif not room: - return (sw_name, "Inconnue", u'Chambre et machine inconnues', VLAN_NOK) + return (sw_name, + "Inconnue", + u'Chambre et machine inconnues', + VLAN_NOK) else: if not room_user: - room_user = User.objects.filter(Q(club__room=port.room) | Q(adherent__room=port.room)) + room_user = User.objects.filter( + Q(club__room=port.room) | Q(adherent__room=port.room) + ) if not room_user: - return (sw_name, room, u'Machine et propriétaire de la chambre inconnus', VLAN_NOK) + return (sw_name, + room, + u'Machine et propriétaire de la chambre ' + 'inconnus', + VLAN_NOK) elif room_user.count() > 1: - return (sw_name, room, u'Machine inconnue, il y a au moins 2 users dans la chambre/local -> ajout de mac automatique impossible', VLAN_NOK) + return (sw_name, + room, + u'Machine inconnue, il y a au moins 2 users ' + 'dans la chambre/local -> ajout de mac ' + 'automatique impossible', + VLAN_NOK) elif not room_user.first().has_access(): - return (sw_name, room, u'Machine inconnue et adhérent non cotisant', VLAN_NOK) + return (sw_name, + room, + u'Machine inconnue et adhérent non cotisant', + VLAN_NOK) else: - result, reason = room_user.first().autoregister_machine(mac_address, nas_type) + result, reason = (room_user + .first() + .autoregister_machine( + mac_address, + nas_type + )) if result: - return (sw_name, room, u'Access Ok, Capture de la mac...' + extra_log, DECISION_VLAN) + return (sw_name, + room, + u'Access Ok, Capture de la mac: ' + extra_log, + DECISION_VLAN) else: - return (sw_name, room, u'Erreur dans le register mac %s' % reason + unicode(mac_address), VLAN_NOK) + return (sw_name, + room, + u'Erreur dans le register mac %s' % ( + reason + unicode(mac_address) + ), + VLAN_NOK) else: room = port.room if not interface.is_active: - return (sw_name, room, u'Machine non active / adherent non cotisant', VLAN_NOK) + return (sw_name, + room, + u'Machine non active / adherent non cotisant', + VLAN_NOK) elif not interface.ipv4: interface.assign_ipv4() - return (sw_name, room, u"Ok, Reassignation de l'ipv4" + extra_log, DECISION_VLAN) + return (sw_name, + room, + u"Ok, Reassignation de l'ipv4" + extra_log, + DECISION_VLAN) else: - return (sw_name, room, u'Machine OK' + extra_log, DECISION_VLAN) + return (sw_name, + room, + u'Machine OK' + extra_log, + DECISION_VLAN) From 0c8bb61574e55233d0099685f32f79d31211f477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Fri, 13 Apr 2018 20:37:04 +0000 Subject: [PATCH 04/22] Pep8 compliance on logs --- logs/acl.py | 1 + logs/templatetags/__init__.py | 1 - logs/templatetags/logs_extra.py | 2 +- logs/views.py | 194 ++++++++++++++++++++------------ 4 files changed, 127 insertions(+), 71 deletions(-) diff --git a/logs/acl.py b/logs/acl.py index 4a1417f6..1ec227d3 100644 --- a/logs/acl.py +++ b/logs/acl.py @@ -26,6 +26,7 @@ Here are defined some functions to check acl on the application. """ + def can_view(user): """Check if an user can view the application. diff --git a/logs/templatetags/__init__.py b/logs/templatetags/__init__.py index fc1be5d7..cd256e09 100644 --- a/logs/templatetags/__init__.py +++ b/logs/templatetags/__init__.py @@ -20,4 +20,3 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - diff --git a/logs/templatetags/logs_extra.py b/logs/templatetags/logs_extra.py index 7bb238ba..e67d8df2 100644 --- a/logs/templatetags/logs_extra.py +++ b/logs/templatetags/logs_extra.py @@ -25,7 +25,7 @@ from django import template register = template.Library() + @register.filter def classname(obj): return obj.__class__.__name__ - diff --git a/logs/views.py b/logs/views.py index e07a2bb9..8763faa6 100644 --- a/logs/views.py +++ b/logs/views.py @@ -227,66 +227,99 @@ def stats_general(request): _all_baned = all_baned() _all_whitelisted = all_whitelisted() _all_active_interfaces_count = all_active_interfaces_count() - _all_active_assigned_interfaces_count = all_active_assigned_interfaces_count() + _all_active_assigned_interfaces_count = \ + all_active_assigned_interfaces_count() stats = [ - [["Categorie", "Nombre d'utilisateurs (total club et adhérents)", "Nombre d'adhérents", "Nombre de clubs"], { - 'active_users': [ - "Users actifs", - User.objects.filter(state=User.STATE_ACTIVE).count(), - Adherent.objects.filter(state=Adherent.STATE_ACTIVE).count(), - Club.objects.filter(state=Club.STATE_ACTIVE).count()], - 'inactive_users': [ - "Users désactivés", - User.objects.filter(state=User.STATE_DISABLED).count(), - Adherent.objects.filter(state=Adherent.STATE_DISABLED).count(), - Club.objects.filter(state=Club.STATE_DISABLED).count()], - 'archive_users': [ - "Users archivés", - User.objects.filter(state=User.STATE_ARCHIVE).count(), - Adherent.objects.filter(state=Adherent.STATE_ARCHIVE).count(), - Club.objects.filter(state=Club.STATE_ARCHIVE).count()], - 'adherent_users': [ - "Cotisant à l'association", - _all_adherent.count(), - _all_adherent.exclude(adherent__isnull=True).count(), - _all_adherent.exclude(club__isnull=True).count()], - 'connexion_users': [ - "Utilisateurs bénéficiant d'une connexion", - _all_has_access.count(), - _all_has_access.exclude(adherent__isnull=True).count(), - _all_has_access.exclude(club__isnull=True).count()], - 'ban_users': [ - "Utilisateurs bannis", - _all_baned.count(), - _all_baned.exclude(adherent__isnull=True).count(), - _all_baned.exclude(club__isnull=True).count()], - 'whitelisted_user': [ - "Utilisateurs bénéficiant d'une connexion gracieuse", - _all_whitelisted.count(), - _all_whitelisted.exclude(adherent__isnull=True).count(), - _all_whitelisted.exclude(club__isnull=True).count()], - 'actives_interfaces': [ - "Interfaces actives (ayant accès au reseau)", - _all_active_interfaces_count.count(), - _all_active_interfaces_count.exclude( - machine__user__adherent__isnull=True - ).count(), - _all_active_interfaces_count.exclude( - machine__user__club__isnull=True - ).count()], - 'actives_assigned_interfaces': [ - "Interfaces actives et assignées ipv4", - _all_active_assigned_interfaces_count.count(), - _all_active_assigned_interfaces_count.exclude( - machine__user__adherent__isnull=True - ).count(), - _all_active_assigned_interfaces_count.exclude( - machine__user__club__isnull=True - ).count()] - }], - [["Range d'ip", "Vlan", "Nombre d'ip totales", "Ip assignées", - "Ip assignées à une machine active", "Ip non assignées"], ip_dict] + [ # First set of data (about users) + [ # Headers + "Categorie", + "Nombre d'utilisateurs (total club et adhérents)", + "Nombre d'adhérents", + "Nombre de clubs" + ], + { # Data + 'active_users': [ + "Users actifs", + User.objects.filter(state=User.STATE_ACTIVE).count(), + (Adherent.objects + .filter(state=Adherent.STATE_ACTIVE) + .count()), + Club.objects.filter(state=Club.STATE_ACTIVE).count() + ], + 'inactive_users': [ + "Users désactivés", + User.objects.filter(state=User.STATE_DISABLED).count(), + (Adherent.objects + .filter(state=Adherent.STATE_DISABLED) + .count()), + Club.objects.filter(state=Club.STATE_DISABLED).count() + ], + 'archive_users': [ + "Users archivés", + User.objects.filter(state=User.STATE_ARCHIVE).count(), + (Adherent.objects + .filter(state=Adherent.STATE_ARCHIVE) + .count()), + Club.objects.filter(state=Club.STATE_ARCHIVE).count() + ], + 'adherent_users': [ + "Cotisant à l'association", + _all_adherent.count(), + _all_adherent.exclude(adherent__isnull=True).count(), + _all_adherent.exclude(club__isnull=True).count() + ], + 'connexion_users': [ + "Utilisateurs bénéficiant d'une connexion", + _all_has_access.count(), + _all_has_access.exclude(adherent__isnull=True).count(), + _all_has_access.exclude(club__isnull=True).count() + ], + 'ban_users': [ + "Utilisateurs bannis", + _all_baned.count(), + _all_baned.exclude(adherent__isnull=True).count(), + _all_baned.exclude(club__isnull=True).count() + ], + 'whitelisted_user': [ + "Utilisateurs bénéficiant d'une connexion gracieuse", + _all_whitelisted.count(), + _all_whitelisted.exclude(adherent__isnull=True).count(), + _all_whitelisted.exclude(club__isnull=True).count() + ], + 'actives_interfaces': [ + "Interfaces actives (ayant accès au reseau)", + _all_active_interfaces_count.count(), + (_all_active_interfaces_count + .exclude(machine__user__adherent__isnull=True) + .count()), + (_all_active_interfaces_count + .exclude(machine__user__club__isnull=True) + .count()) + ], + 'actives_assigned_interfaces': [ + "Interfaces actives et assignées ipv4", + _all_active_assigned_interfaces_count.count(), + (_all_active_assigned_interfaces_count + .exclude(machine__user__adherent__isnull=True) + .count()), + (_all_active_assigned_interfaces_count + .exclude(machine__user__club__isnull=True) + .count()) + ] + } + ], + [ # Second set of data (about ip adresses) + [ # Headers + "Range d'ip", + "Vlan", + "Nombre d'ip totales", + "Ip assignées", + "Ip assignées à une machine active", + "Ip non assignées" + ], + ip_dict # Data already prepared ] + ] return render(request, 'logs/stats_general.html', {'stats_list': stats}) @@ -313,11 +346,26 @@ def stats_models(request): 'whitelist': [Whitelist.PRETTY_NAME, Whitelist.objects.count()] }, 'Cotisations': { - 'factures': [Facture._meta.verbose_name.title(), Facture.objects.count()], - 'vente': [Vente._meta.verbose_name.title(), Vente.objects.count()], - 'cotisation': [Cotisation._meta.verbose_name.title(), Cotisation.objects.count()], - 'article': [Article._meta.verbose_name.title(), Article.objects.count()], - 'banque': [Banque._meta.verbose_name.title(), Banque.objects.count()], + 'factures': [ + Facture._meta.verbose_name.title(), + Facture.objects.count() + ], + 'vente': [ + Vente._meta.verbose_name.title(), + Vente.objects.count() + ], + 'cotisation': [ + Cotisation._meta.verbose_name.title(), + Cotisation.objects.count() + ], + 'article': [ + Article._meta.verbose_name.title(), + Article.objects.count() + ], + 'banque': [ + Banque._meta.verbose_name.title(), + Banque.objects.count() + ], }, 'Machines': { 'machine': [Machine.PRETTY_NAME, Machine.objects.count()], @@ -432,14 +480,22 @@ def stats_actions(request): } return render(request, 'logs/stats_users.html', {'stats_list': stats}) + @login_required @can_view_app('users') def stats_droits(request): """Affiche la liste des droits et les users ayant chaque droit""" - depart=time() - stats_list={} - - for droit in ListRight.objects.all().select_related('group_ptr'): - stats_list[droit]=droit.user_set.all().annotate(num=Count('revision'),last=Max('revision__date_created')) + depart = time() + stats_list = {} - return render(request, 'logs/stats_droits.html', {'stats_list': stats_list}) + for droit in ListRight.objects.all().select_related('group_ptr'): + stats_list[droit] = droit.user_set.all().annotate( + num=Count('revision'), + last=Max('revision__date_created') + ) + + return render( + request, + 'logs/stats_droits.html', + {'stats_list': stats_list} + ) From 287ce23d1c83fe9b21596d4b72e84bac4a3b5036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Fri, 13 Apr 2018 22:48:44 +0000 Subject: [PATCH 05/22] Pep8 compliance on machines --- machines/acl.py | 1 + machines/admin.py | 4 +- machines/forms.py | 10 +- machines/models.py | 313 +++++++++----- machines/serializers.py | 32 +- machines/urls.py | 105 +++-- machines/views.py | 893 +++++++++++++++++++++++++++++++--------- 7 files changed, 1010 insertions(+), 348 deletions(-) diff --git a/machines/acl.py b/machines/acl.py index f77a93c7..1b74760c 100644 --- a/machines/acl.py +++ b/machines/acl.py @@ -26,6 +26,7 @@ Here are defined some functions to check acl on the application. """ + def can_view(user): """Check if an user can view the application. diff --git a/machines/admin.py b/machines/admin.py index 9a2d5133..76121bd5 100644 --- a/machines/admin.py +++ b/machines/admin.py @@ -104,7 +104,7 @@ class OuverturePortListAdmin(VersionAdmin): class InterfaceAdmin(VersionAdmin): - list_display = ('machine','type','mac_address','ipv4','details') + list_display = ('machine', 'type', 'mac_address', 'ipv4', 'details') class DomainAdmin(VersionAdmin): @@ -133,5 +133,3 @@ admin.site.register(Ipv6List, Ipv6ListAdmin) admin.site.register(Nas, NasAdmin) admin.site.register(OuverturePort, OuverturePortAdmin) admin.site.register(OuverturePortList, OuverturePortListAdmin) - - diff --git a/machines/forms.py b/machines/forms.py index 6ece03e8..8133f622 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -94,7 +94,8 @@ class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): self.fields['type'].label = 'Type de machine' self.fields['type'].empty_label = "Séléctionner un type de machine" if "ipv4" in self.fields: - self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4" + self.fields['ipv4'].empty_label = ("Assignation automatique de " + "l'ipv4") self.fields['ipv4'].queryset = IpList.objects.filter( interface__isnull=True ) @@ -328,6 +329,7 @@ class MxForm(FormRevMixin, ModelForm): interface_parent=None ).select_related('extension') + class DelMxForm(FormRevMixin, Form): """Suppression d'un ou plusieurs MX""" mx = forms.ModelMultipleChoiceField( @@ -472,8 +474,10 @@ class ServiceForm(FormRevMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['servers'].queryset = Interface.objects.all()\ - .select_related('domain__extension') + self.fields['servers'].queryset = (Interface.objects.all() + .select_related( + 'domain__extension' + )) def save(self, commit=True): instance = super(ServiceForm, self).save(commit=False) diff --git a/machines/models.py b/machines/models.py index 2b4eb4a2..361c7bc1 100644 --- a/machines/models.py +++ b/machines/models.py @@ -40,7 +40,7 @@ from django.core.validators import MaxValueValidator from macaddress.fields import MACAddressField from re2o.field_permissions import FieldPermissionModelMixin -from re2o.mixins import AclMixin, RevMixin +from re2o.mixins import AclMixin, RevMixin import users.models import preferences.models @@ -63,7 +63,8 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): class Meta: permissions = ( ("view_machine", "Peut voir un objet machine quelquonque"), - ("change_machine_user", "Peut changer le propriétaire d'une machine"), + ("change_machine_user", + "Peut changer le propriétaire d'une machine"), ) def get_instance(machineid, *args, **kwargs): @@ -76,7 +77,12 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): def linked_objects(self): """Return linked objects : machine and domain. Usefull in history display""" - return chain(self.interface_set.all(), Domain.objects.filter(interface_parent__in=self.interface_set.all())) + return chain( + self.interface_set.all(), + Domain.objects.filter( + interface_parent__in=self.interface_set.all() + ) + ) @staticmethod def can_change_user(user_request, *args, **kwargs): @@ -90,7 +96,8 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): A tuple with a boolean stating if edition is allowed and an explanation message. """ - return user_request.has_perm('machines.change_machine_user'), "Vous ne pouvez pas modifier l'utilisateur de la machine." + return (user_request.has_perm('machines.change_machine_user'), + "Vous ne pouvez pas modifier l'utilisateur de la machine.") def can_view_all(user_request, *args, **kwargs): """Vérifie qu'on peut bien afficher l'ensemble des machines, @@ -98,7 +105,8 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" if not user_request.has_perm('machines.view_machine'): - return False, u"Vous ne pouvez pas afficher l'ensemble des machines sans permission" + return False, (u"Vous ne pouvez pas afficher l'ensemble des " + "machines sans permission") return True, None def can_create(user_request, userid, *args, **kwargs): @@ -111,17 +119,21 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): user = users.models.User.objects.get(pk=userid) except users.models.User.DoesNotExist: return False, u"Utilisateur inexistant" - 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 not user_request.has_perm('machines.add_machine'): - if not preferences.models.OptionalMachine.get_cached_value('create_machine'): + if not (preferences.models.OptionalMachine + .get_cached_value('create_machine')): return False, u"Vous ne pouvez pas ajouter une machine" if user != user_request: - return False, u"Vous ne pouvez pas ajouter une machine à un\ - autre user que vous sans droit" + return False, (u"Vous ne pouvez pas ajouter une machine à un " + "autre user que vous sans droit") if user.user_interfaces().count() >= max_lambdauser_interfaces: - return False, u"Vous avez atteint le maximum d'interfaces\ - autorisées que vous pouvez créer vous même (%s) "\ - % max_lambdauser_interfaces + return False, (u"Vous avez atteint le maximum d'interfaces " + "autorisées que vous pouvez créer vous même " + "(%s) " % max_lambdauser_interfaces) return True, None def can_edit(self, user_request, *args, **kwargs): @@ -131,9 +143,15 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): :param user_request: instance user qui fait l'edition :return: True ou False avec la raison le cas échéant""" if self.user != user_request: - if not user_request.has_perm('machines.change_interface') or not self.user.can_edit(self.user, user_request, *args, **kwargs)[0]: - return False, u"Vous ne pouvez pas éditer une machine\ - d'un autre user que vous sans droit" + if (not user_request.has_perm('machines.change_interface') or + not self.user.can_edit( + self.user, + user_request, + *args, + **kwargs + )[0]): + return False, (u"Vous ne pouvez pas éditer une machine " + "d'un autre user que vous sans droit") return True, None def can_delete(self, user_request, *args, **kwargs): @@ -143,9 +161,15 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" if self.user != user_request: - if not user_request.has_perm('machines.change_interface') or not self.user.can_edit(self.user, user_request, *args, **kwargs)[0]: - return False, u"Vous ne pouvez pas éditer une machine\ - d'un autre user que vous sans droit" + if (not user_request.has_perm('machines.change_interface') or + not self.user.can_edit( + self.user, + user_request, + *args, + **kwargs + )[0]): + return False, (u"Vous ne pouvez pas éditer une machine " + "d'un autre user que vous sans droit") return True, None def can_view(self, user_request, *args, **kwargs): @@ -154,15 +178,16 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): :param self: instance machine à éditer :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - if not user_request.has_perm('machines.view_machine') and self.user != user_request: - return False, u"Vous n'avez pas droit de voir les machines autre\ - que les vôtres" + if (not user_request.has_perm('machines.view_machine') and + self.user != user_request): + return False, (u"Vous n'avez pas droit de voir les machines autre " + "que les vôtres") return True, None def __init__(self, *args, **kwargs): super(Machine, self).__init__(*args, **kwargs) self.field_permissions = { - 'user' : self.can_change_user, + 'user': self.can_change_user, } def __str__(self): @@ -184,7 +209,8 @@ class MachineType(RevMixin, AclMixin, models.Model): class Meta: permissions = ( ("view_machinetype", "Peut voir un objet machinetype"), - ("use_all_machinetype", "Peut utiliser n'importe quel type de machine"), + ("use_all_machinetype", + "Peut utiliser n'importe quel type de machine"), ) def all_interfaces(self): @@ -202,7 +228,8 @@ class MachineType(RevMixin, AclMixin, models.Model): message is acces is not allowed. """ if not user_request.has_perm('machines.use_all_machinetype'): - return False, u"Vous n'avez pas le droit d'utiliser tout types de machines" + return False, (u"Vous n'avez pas le droit d'utiliser tout types " + "de machines") return True, None def __str__(self): @@ -300,7 +327,11 @@ class IpType(RevMixin, AclMixin, models.Model): if not self.prefix_v6: return else: - for ipv6 in Ipv6List.objects.filter(interface__in=Interface.objects.filter(type__in=MachineType.objects.filter(ip_type=self))): + for ipv6 in Ipv6List.objects.filter( + interface__in=Interface.objects.filter( + type__in=MachineType.objects.filter(ip_type=self) + ) + ): ipv6.check_and_replace_prefix(prefix=self.prefix_v6) def clean(self): @@ -330,7 +361,8 @@ class IpType(RevMixin, AclMixin, models.Model): super(IpType, self).save(*args, **kwargs) def can_use_all(user_request, *args, **kwargs): - """Superdroit qui permet d'utiliser toutes les extensions sans restrictions + """Superdroit qui permet d'utiliser toutes les extensions sans + restrictions :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" return user_request.has_perm('machines.use_all_iptype'), None @@ -409,17 +441,17 @@ class SOA(RevMixin, AclMixin, models.Model): help_text='Email du contact pour la zone' ) refresh = models.PositiveIntegerField( - default=86400, # 24 hours + default=86400, # 24 hours help_text='Secondes avant que les DNS secondaires doivent demander le\ serial du DNS primaire pour détecter une modification' ) retry = models.PositiveIntegerField( - default=7200, # 2 hours + default=7200, # 2 hours help_text='Secondes avant que les DNS secondaires fassent une nouvelle\ demande de serial en cas de timeout du DNS primaire' ) expire = models.PositiveIntegerField( - default=3600000, # 1000 hours + default=3600000, # 1000 hours help_text='Secondes après lesquelles les DNS secondaires arrêtent de\ de répondre aux requêtes en cas de timeout du DNS primaire' ) @@ -469,8 +501,10 @@ class SOA(RevMixin, AclMixin, models.Model): extensions . /!\ Ne jamais supprimer ou renommer cette fonction car elle est utilisée dans les migrations de la BDD. """ - return cls.objects.get_or_create(name="SOA to edit", mail="postmaser@example.com")[0].pk - + return cls.objects.get_or_create( + name="SOA to edit", + mail="postmaser@example.com" + )[0].pk class Extension(RevMixin, AclMixin, models.Model): @@ -522,7 +556,8 @@ class Extension(RevMixin, AclMixin, models.Model): return entry def can_use_all(user_request, *args, **kwargs): - """Superdroit qui permet d'utiliser toutes les extensions sans restrictions + """Superdroit qui permet d'utiliser toutes les extensions sans + restrictions :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" return user_request.has_perm('machines.use_all_extension'), None @@ -555,7 +590,10 @@ class Mx(RevMixin, AclMixin, models.Model): def dns_entry(self): """Renvoie l'entrée DNS complète pour un MX à mettre dans les fichiers de zones""" - return "@ IN MX " + str(self.priority).ljust(3) + " " + str(self.name) + return "@ IN MX {prior} {name}".format( + prior=str(self.priority).ljust(3), + name=str(name) + ) def __str__(self): return str(self.zone) + ' ' + str(self.priority) + ' ' + str(self.name) @@ -611,7 +649,7 @@ class Srv(RevMixin, AclMixin, models.Model): TCP = 'TCP' UDP = 'UDP' - service = models.CharField(max_length=31) + service = models.CharField(max_length=31) protocole = models.CharField( max_length=3, choices=( @@ -628,9 +666,9 @@ class Srv(RevMixin, AclMixin, models.Model): priority = models.PositiveIntegerField( default=0, validators=[MaxValueValidator(65535)], - help_text="La priorité du serveur cible (valeur entière non négative,\ - plus elle est faible, plus ce serveur sera utilisé s'il est disponible)" - + help_text=("La priorité du serveur cible (valeur entière non " + "négative, plus elle est faible, plus ce serveur sera " + "utilisé s'il est disponible)") ) weight = models.PositiveIntegerField( default=0, @@ -667,7 +705,7 @@ class Srv(RevMixin, AclMixin, models.Model): str(self.port) + ' ' + str(self.target) + '.' -class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): +class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): """ Une interface. Objet clef de l'application machine : - une address mac unique. Possibilité de la rendre unique avec le typemachine @@ -692,7 +730,8 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): class Meta: permissions = ( ("view_interface", "Peut voir un objet interface"), - ("change_interface_machine", "Peut changer le propriétaire d'une interface"), + ("change_interface_machine", + "Peut changer le propriétaire d'une interface"), ) @cached_property @@ -719,7 +758,10 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): prefix_v6 = self.type.ip_type.prefix_v6 if not prefix_v6: return None - return IPv6Address(IPv6Address(prefix_v6).exploded[:20] + IPv6Address(self.id).exploded[20:]) + return IPv6Address( + IPv6Address(prefix_v6).exploded[:20] + + IPv6Address(self.id).exploded[20:] + ) def sync_ipv6_dhcpv6(self): """Affecte une ipv6 dhcpv6 calculée à partir de l'id de la machine""" @@ -741,7 +783,9 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): ipv6_slaac = self.ipv6_slaac if not ipv6_slaac: return - ipv6_object = Ipv6List.objects.filter(interface=self, slaac_ip=True).first() + ipv6_object = (Ipv6List.objects + .filter(interface=self, slaac_ip=True) + .first()) if not ipv6_object: ipv6_object = Ipv6List(interface=self, slaac_ip=True) if ipv6_object.ipv6 != str(ipv6_slaac): @@ -750,19 +794,24 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): def sync_ipv6(self): """Cree et met à jour l'ensemble des ipv6 en fonction du mode choisi""" - if preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'SLAAC': + if (preferences.models.OptionalMachine + .get_cached_value('ipv6_mode') == 'SLAAC'): self.sync_ipv6_slaac() - elif preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'DHCPV6': + elif (preferences.models.OptionalMachine + .get_cached_value('ipv6_mode') == 'DHCPV6'): self.sync_ipv6_dhcpv6() else: return def ipv6(self): """ Renvoie le queryset de la liste des ipv6 - On renvoie l'ipv6 slaac que si le mode slaac est activé (et non dhcpv6)""" - if preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'SLAAC': + On renvoie l'ipv6 slaac que si le mode slaac est activé + (et non dhcpv6)""" + if (preferences.models.OptionalMachine + .get_cached_value('ipv6_mode') == 'SLAAC'): return self.ipv6list.all() - elif preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'DHCPV6': + elif (preferences.models.OptionalMachine + .get_cached_value('ipv6_mode') == 'DHCPV6'): return self.ipv6list.filter(slaac_ip=False) else: return None @@ -789,7 +838,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): # instance. # But in our case, it's impossible to create a type value so we raise # the error. - if not hasattr(self, 'type') : + if not hasattr(self, 'type'): raise ValidationError("Le type d'ip choisi n'est pas valide") self.filter_macaddress() self.mac_address = str(EUI(self.mac_address)) or None @@ -836,13 +885,18 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): except Machine.DoesNotExist: return False, u"Machine inexistante" if not user_request.has_perm('machines.add_interface'): - if not preferences.models.OptionalMachine.get_cached_value('create_machine'): + if not (preferences.models.OptionalMachine + .get_cached_value('create_machine')): return False, u"Vous ne pouvez pas ajouter une machine" - 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: return False, u"Vous ne pouvez pas ajouter une interface à une\ machine d'un autre user que vous sans droit" - if machine.user.user_interfaces().count() >= max_lambdauser_interfaces: + if (machine.user.user_interfaces().count() >= + max_lambdauser_interfaces): return False, u"Vous avez atteint le maximum d'interfaces\ autorisées que vous pouvez créer vous même (%s) "\ % max_lambdauser_interfaces @@ -850,7 +904,8 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): @staticmethod def can_change_machine(user_request, *args, **kwargs): - return user_request.has_perm('machines.change_interface_machine'), "Droit requis pour changer la machine" + return (user_request.has_perm('machines.change_interface_machine'), + "Droit requis pour changer la machine") def can_edit(self, user_request, *args, **kwargs): """Verifie que l'user a les bons droits infra pour editer @@ -859,9 +914,14 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" if self.machine.user != user_request: - if not user_request.has_perm('machines.change_interface') or not self.machine.user.can_edit(user_request, *args, **kwargs)[0]: - return False, u"Vous ne pouvez pas éditer une machine\ - d'un autre user que vous sans droit" + if (not user_request.has_perm('machines.change_interface') or + not self.machine.user.can_edit( + user_request, + *args, + **kwargs + )[0]): + return False, (u"Vous ne pouvez pas éditer une machine " + "d'un autre user que vous sans droit") return True, None def can_delete(self, user_request, *args, **kwargs): @@ -871,9 +931,14 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" if self.machine.user != user_request: - if not user_request.has_perm('machines.change_interface') or not self.machine.user.can_edit(user_request, *args, **kwargs)[0]: - return False, u"Vous ne pouvez pas éditer une machine\ - d'un autre user que vous sans droit" + if (not user_request.has_perm('machines.change_interface') or + not self.machine.user.can_edit( + user_request, + *args, + **kwargs + )[0]): + return False, (u"Vous ne pouvez pas éditer une machine " + "d'un autre user que vous sans droit") return True, None def can_view(self, user_request, *args, **kwargs): @@ -882,15 +947,16 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model): :param self: instance interface à voir :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - if not user_request.has_perm('machines.view_interface') and self.machine.user != user_request: - return False, u"Vous n'avez pas le droit de voir des machines autre\ - que les vôtres" + if (not user_request.has_perm('machines.view_interface') and + self.machine.user != user_request): + return False, (u"Vous n'avez pas le droit de voir des machines " + "autre que les vôtres") return True, None def __init__(self, *args, **kwargs): super(Interface, self).__init__(*args, **kwargs) self.field_permissions = { - 'machine' : self.can_change_machine, + 'machine': self.can_change_machine, } def __str__(self): @@ -921,13 +987,18 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): protocol='IPv6', unique=True ) - interface = models.ForeignKey('Interface', on_delete=models.CASCADE, related_name='ipv6list') + interface = models.ForeignKey( + 'Interface', + on_delete=models.CASCADE, + related_name='ipv6list' + ) slaac_ip = models.BooleanField(default=False) class Meta: permissions = ( ("view_ipv6list", "Peut voir un objet ipv6"), - ("change_ipv6list_slaac_ip", "Peut changer la valeur slaac sur une ipv6"), + ("change_ipv6list_slaac_ip", + "Peut changer la valeur slaac sur une ipv6"), ) def can_create(user_request, interfaceid, *args, **kwargs): @@ -948,7 +1019,8 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): @staticmethod def can_change_slaac_ip(user_request, *args, **kwargs): - return user_request.has_perm('machines.change_ipv6list_slaac_ip'), "Droit requis pour changer la valeur slaac ip" + return (user_request.has_perm('machines.change_ipv6list_slaac_ip'), + "Droit requis pour changer la valeur slaac ip") def can_edit(self, user_request, *args, **kwargs): """Verifie que l'user a les bons droits infra pour editer @@ -957,9 +1029,14 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" if self.interface.machine.user != user_request: - if not user_request.has_perm('machines.change_ipv6list') or not self.interface.machine.user.can_edit(user_request, *args, **kwargs)[0]: - return False, u"Vous ne pouvez pas éditer une machine\ - d'un autre user que vous sans droit" + if (not user_request.has_perm('machines.change_ipv6list') or + not self.interface.machine.user.can_edit( + user_request, + *args, + **kwargs + )[0]): + return False, (u"Vous ne pouvez pas éditer une machine " + "d'un autre user que vous sans droit") return True, None def can_delete(self, user_request, *args, **kwargs): @@ -969,9 +1046,14 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" if self.interface.machine.user != user_request: - if not user_request.has_perm('machines.change_ipv6list') or not self.interface.machine.user.can_edit(user_request, *args, **kwargs)[0]: - return False, u"Vous ne pouvez pas éditer une machine\ - d'un autre user que vous sans droit" + if (not user_request.has_perm('machines.change_ipv6list') or + not self.interface.machine.user.can_edit( + user_request, + *args, + **kwargs + )[0]): + return False, (u"Vous ne pouvez pas éditer une machine " + "d'un autre user que vous sans droit") return True, None def can_view(self, user_request, *args, **kwargs): @@ -980,15 +1062,16 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): :param self: instance interface à voir :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - if not user_request.has_perm('machines.view_ipv6list') and self.interface.machine.user != user_request: - return False, u"Vous n'avez pas le droit de voir des machines autre\ - que les vôtres" + if (not user_request.has_perm('machines.view_ipv6list') and + self.interface.machine.user != user_request): + return False, (u"Vous n'avez pas le droit de voir des machines " + "autre que les vôtres") return True, None def __init__(self, *args, **kwargs): super(Ipv6List, self).__init__(*args, **kwargs) self.field_permissions = { - 'slaac_ip' : self.can_change_slaac_ip, + 'slaac_ip': self.can_change_slaac_ip, } def check_and_replace_prefix(self, prefix=None): @@ -996,17 +1079,27 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): prefix_v6 = prefix or self.interface.type.ip_type.prefix_v6 if not prefix_v6: return - if IPv6Address(self.ipv6).exploded[:20] != IPv6Address(prefix_v6).exploded[:20]: - self.ipv6 = IPv6Address(IPv6Address(prefix_v6).exploded[:20] + IPv6Address(self.ipv6).exploded[20:]) + if (IPv6Address(self.ipv6).exploded[:20] != + IPv6Address(prefix_v6).exploded[:20]): + self.ipv6 = IPv6Address( + IPv6Address(prefix_v6).exploded[:20] + + IPv6Address(self.ipv6).exploded[20:] + ) self.save() def clean(self, *args, **kwargs): - if self.slaac_ip and Ipv6List.objects.filter(interface=self.interface, slaac_ip=True).exclude(id=self.id): + if self.slaac_ip and (Ipv6List.objects + .filter(interface=self.interface, slaac_ip=True) + .exclude(id=self.id)): raise ValidationError("Une ip slaac est déjà enregistrée") prefix_v6 = self.interface.type.ip_type.prefix_v6 if prefix_v6: - if IPv6Address(self.ipv6).exploded[:20] != IPv6Address(prefix_v6).exploded[:20]: - raise ValidationError("Le prefixv6 est incorrect et ne correspond pas au type associé à la machine") + if (IPv6Address(self.ipv6).exploded[:20] != + IPv6Address(prefix_v6).exploded[:20]): + raise ValidationError( + "Le prefixv6 est incorrect et ne correspond pas au type " + "associé à la machine" + ) super(Ipv6List, self).clean(*args, **kwargs) def save(self, *args, **kwargs): @@ -1089,7 +1182,10 @@ class Domain(RevMixin, AclMixin, models.Model): def dns_entry(self): """ Une entrée DNS""" if self.cname: - return str(self.name).ljust(15) + " IN CNAME " + str(self.cname) + "." + return "{name} IN CNAME {cname}.".format( + name=str(self.name).ljust(15), + cname=str(self.cname) + ) def save(self, *args, **kwargs): """ Empèche le save sans extension valide. @@ -1122,18 +1218,22 @@ class Domain(RevMixin, AclMixin, models.Model): except Interface.DoesNotExist: return False, u"Interface inexistante" 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: - return False, u"Vous ne pouvez pas ajouter un alias à une\ - machine d'un autre user que vous sans droit" + return False, (u"Vous ne pouvez pas ajouter un alias à une " + "machine d'un autre user que vous sans droit") if Domain.objects.filter( - cname__in=Domain.objects.filter( - interface_parent__in=interface.machine.user.user_interfaces() - ) - ).count() >= max_lambdauser_aliases: - return False, u"Vous avez atteint le maximum d'alias\ - autorisés que vous pouvez créer vous même (%s) "\ - % max_lambdauser_aliases + cname__in=Domain.objects.filter( + interface_parent__in=(interface.machine.user + .user_interfaces()) + ) + ).count() >= max_lambdauser_aliases: + return False, (u"Vous avez atteint le maximum d'alias " + "autorisés que vous pouvez créer vous même " + "(%s) " % max_lambdauser_aliases) return True, None def can_edit(self, user_request, *args, **kwargs): @@ -1142,10 +1242,10 @@ class Domain(RevMixin, AclMixin, models.Model): :param self: Instance domain à editer :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" - if not user_request.has_perm('machines.change_domain') and\ - self.get_source_interface.machine.user != user_request: - return False, u"Vous ne pouvez pas editer un alias à une machine\ - d'un autre user que vous sans droit" + if (not user_request.has_perm('machines.change_domain') and + self.get_source_interface.machine.user != user_request): + return False, (u"Vous ne pouvez pas editer un alias à une machine " + "d'un autre user que vous sans droit") return True, None def can_delete(self, user_request, *args, **kwargs): @@ -1154,10 +1254,10 @@ class Domain(RevMixin, AclMixin, models.Model): :param self: Instance domain à del :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" - if not user_request.has_perm('machines.delete_domain') and\ - self.get_source_interface.machine.user != user_request: - return False, u"Vous ne pouvez pas supprimer un alias à une machine\ - d'un autre user que vous sans droit" + if (not user_request.has_perm('machines.delete_domain') and + self.get_source_interface.machine.user != user_request): + return False, (u"Vous ne pouvez pas supprimer un alias à une " + "machine d'un autre user que vous sans droit") return True, None def can_view(self, user_request, *args, **kwargs): @@ -1166,10 +1266,10 @@ class Domain(RevMixin, AclMixin, models.Model): :param self: instance domain à voir :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - if not user_request.has_perm('machines.view_domain') and\ - self.get_source_interface.machine.user != user_request: - return False, u"Vous n'avez pas le droit de voir des machines autre\ - que les vôtres" + if (not user_request.has_perm('machines.view_domain') and + self.get_source_interface.machine.user != user_request): + return False, (u"Vous n'avez pas le droit de voir des machines " + "autre que les vôtres") return True, None def __str__(self): @@ -1238,8 +1338,8 @@ class Service(RevMixin, AclMixin, models.Model): """ Django ne peut créer lui meme les relations manytomany avec table intermediaire explicite""" for serv in servers.exclude( - pk__in=Interface.objects.filter(service=self) - ): + pk__in=Interface.objects.filter(service=self) + ): link = Service_link(service=self, server=serv) link.save() Service_link.objects.filter(service=self).exclude(server__in=servers)\ @@ -1314,8 +1414,8 @@ class OuverturePortList(RevMixin, AclMixin, models.Model): :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" if not user_request.has_perm('machines.delete_ouvertureportlist'): - return False, u"Vous n'avez pas le droit de supprimer une ouverture\ - de port" + return False, (u"Vous n'avez pas le droit de supprimer une " + "ouverture de port") if self.interface_set.all(): return False, u"Cette liste de ports est utilisée" return True, None @@ -1542,4 +1642,3 @@ def srv_post_save(sender, **kwargs): def srv_post_delete(sender, **kwargs): """Regeneration dns après modification d'un SRV""" regen('dns') - diff --git a/machines/serializers.py b/machines/serializers.py index 42ca679d..cdd6b705 100644 --- a/machines/serializers.py +++ b/machines/serializers.py @@ -21,7 +21,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#Augustin Lemesle +# Augustin Lemesle from rest_framework import serializers from machines.models import ( @@ -181,7 +181,7 @@ class ExtensionSerializer(serializers.ModelSerializer): return str(obj.dns_entry) def get_soa_data(self, obj): - return { 'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param } + return {'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param} class MxSerializer(serializers.ModelSerializer): @@ -316,23 +316,23 @@ class OuverturePortsSerializer(serializers.Serializer): ipv6 = serializers.SerializerMethodField() def get_ipv4(): - return {i.ipv4.ipv4: - { - "tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()], - "tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()], - "udp_in":[j.udp_ports_in() for j in i.port_lists.all()], - "udp_out":[j.udp_ports_out() for j in i.port_lists.all()], + return { + i.ipv4.ipv4: { + "tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()], + "tcp_out": [j.tcp_ports_out()for j in i.port_lists.all()], + "udp_in": [j.udp_ports_in() for j in i.port_lists.all()], + "udp_out": [j.udp_ports_out() for j in i.port_lists.all()], } - for i in Interface.objects.all() if i.ipv4 + for i in Interface.objects.all() if i.ipv4 } def get_ipv6(): - return {i.ipv6: - { - "tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()], - "tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()], - "udp_in":[j.udp_ports_in() for j in i.port_lists.all()], - "udp_out":[j.udp_ports_out() for j in i.port_lists.all()], + return { + i.ipv6: { + "tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()], + "tcp_out": [j.tcp_ports_out()for j in i.port_lists.all()], + "udp_in": [j.udp_ports_in() for j in i.port_lists.all()], + "udp_out": [j.udp_ports_out() for j in i.port_lists.all()], } - for i in Interface.objects.all() if i.ipv6 + for i in Interface.objects.all() if i.ipv6 } diff --git a/machines/urls.py b/machines/urls.py index e3454097..c9afd56c 100644 --- a/machines/urls.py +++ b/machines/urls.py @@ -28,21 +28,39 @@ import re2o from . import views urlpatterns = [ - url(r'^new_machine/(?P[0-9]+)$', views.new_machine, name='new-machine'), - url(r'^edit_interface/(?P[0-9]+)$', views.edit_interface, name='edit-interface'), - url(r'^del_machine/(?P[0-9]+)$', views.del_machine, name='del-machine'), - url(r'^new_interface/(?P[0-9]+)$', views.new_interface, name='new-interface'), - url(r'^del_interface/(?P[0-9]+)$', views.del_interface, name='del-interface'), + url(r'^new_machine/(?P[0-9]+)$', + views.new_machine, + name='new-machine'), + url(r'^edit_interface/(?P[0-9]+)$', + views.edit_interface, + name='edit-interface'), + url(r'^del_machine/(?P[0-9]+)$', + views.del_machine, + name='del-machine'), + url(r'^new_interface/(?P[0-9]+)$', + views.new_interface, + name='new-interface'), + url(r'^del_interface/(?P[0-9]+)$', + views.del_interface, + name='del-interface'), url(r'^add_machinetype/$', views.add_machinetype, name='add-machinetype'), - url(r'^edit_machinetype/(?P[0-9]+)$', views.edit_machinetype, name='edit-machinetype'), + url(r'^edit_machinetype/(?P[0-9]+)$', + views.edit_machinetype, + name='edit-machinetype'), url(r'^del_machinetype/$', views.del_machinetype, name='del-machinetype'), - url(r'^index_machinetype/$', views.index_machinetype, name='index-machinetype'), + url(r'^index_machinetype/$', + views.index_machinetype, + name='index-machinetype'), url(r'^add_iptype/$', views.add_iptype, name='add-iptype'), - url(r'^edit_iptype/(?P[0-9]+)$', views.edit_iptype, name='edit-iptype'), + url(r'^edit_iptype/(?P[0-9]+)$', + views.edit_iptype, + name='edit-iptype'), url(r'^del_iptype/$', views.del_iptype, name='del-iptype'), url(r'^index_iptype/$', views.index_iptype, name='index-iptype'), url(r'^add_extension/$', views.add_extension, name='add-extension'), - url(r'^edit_extension/(?P[0-9]+)$', views.edit_extension, name='edit-extension'), + url(r'^edit_extension/(?P[0-9]+)$', + views.edit_extension, + name='edit-extension'), url(r'^del_extension/$', views.del_extension, name='del-extension'), url(r'^add_soa/$', views.add_soa, name='add-soa'), url(r'^edit_soa/(?P[0-9]+)$', views.edit_soa, name='edit-soa'), @@ -60,16 +78,34 @@ urlpatterns = [ url(r'^edit_srv/(?P[0-9]+)$', views.edit_srv, name='edit-srv'), url(r'^del_srv/$', views.del_srv, name='del-srv'), url(r'^index_extension/$', views.index_extension, name='index-extension'), - url(r'^add_alias/(?P[0-9]+)$', views.add_alias, name='add-alias'), - url(r'^edit_alias/(?P[0-9]+)$', views.edit_alias, name='edit-alias'), - url(r'^del_alias/(?P[0-9]+)$', views.del_alias, name='del-alias'), - url(r'^index_alias/(?P[0-9]+)$', views.index_alias, name='index-alias'), - url(r'^new_ipv6list/(?P[0-9]+)$', views.new_ipv6list, name='new-ipv6list'), - url(r'^edit_ipv6list/(?P[0-9]+)$', views.edit_ipv6list, name='edit-ipv6list'), - url(r'^del_ipv6list/(?P[0-9]+)$', views.del_ipv6list, name='del-ipv6list'), - url(r'^index_ipv6/(?P[0-9]+)$', views.index_ipv6, name='index-ipv6'), + url(r'^add_alias/(?P[0-9]+)$', + views.add_alias, + name='add-alias'), + url(r'^edit_alias/(?P[0-9]+)$', + views.edit_alias, + name='edit-alias'), + url(r'^del_alias/(?P[0-9]+)$', + views.del_alias, + name='del-alias'), + url(r'^index_alias/(?P[0-9]+)$', + views.index_alias, + name='index-alias'), + url(r'^new_ipv6list/(?P[0-9]+)$', + views.new_ipv6list, + name='new-ipv6list'), + url(r'^edit_ipv6list/(?P[0-9]+)$', + views.edit_ipv6list, + name='edit-ipv6list'), + url(r'^del_ipv6list/(?P[0-9]+)$', + views.del_ipv6list, + name='del-ipv6list'), + url(r'^index_ipv6/(?P[0-9]+)$', + views.index_ipv6, + name='index-ipv6'), url(r'^add_service/$', views.add_service, name='add-service'), - url(r'^edit_service/(?P[0-9]+)$', views.edit_service, name='edit-service'), + url(r'^edit_service/(?P[0-9]+)$', + views.edit_service, + name='edit-service'), url(r'^del_service/$', views.del_service, name='del-service'), url(r'^index_service/$', views.index_service, name='index-service'), url(r'^add_vlan/$', views.add_vlan, name='add-vlan'), @@ -80,15 +116,15 @@ urlpatterns = [ url(r'^edit_nas/(?P[0-9]+)$', views.edit_nas, name='edit-nas'), url(r'^del_nas/$', views.del_nas, name='del-nas'), url(r'^index_nas/$', views.index_nas, name='index-nas'), - url( - r'history/(?P\w+)/(?P[0-9]+)$', + url(r'history/(?P\w+)/(?P[0-9]+)$', re2o.views.history, name='history', - kwargs={'application':'machines'}, - ), + kwargs={'application': 'machines'}), url(r'^$', views.index, name='index'), url(r'^rest/mac-ip/$', views.mac_ip, name='mac-ip'), - url(r'^rest/regen-achieved/$', views.regen_achieved, name='regen-achieved'), + url(r'^rest/regen-achieved/$', + views.regen_achieved, + name='regen-achieved'), url(r'^rest/mac-ip-dns/$', views.mac_ip_dns, name='mac-ip-dns'), url(r'^rest/alias/$', views.alias, name='alias'), url(r'^rest/corresp/$', views.corresp, name='corresp'), @@ -97,12 +133,21 @@ urlpatterns = [ url(r'^rest/txt/$', views.txt, name='txt'), url(r'^rest/srv/$', views.srv, name='srv'), url(r'^rest/zones/$', views.zones, name='zones'), - url(r'^rest/service_servers/$', views.service_servers, name='service-servers'), - url(r'^rest/ouverture_ports/$', views.ouverture_ports, name='ouverture-ports'), + url(r'^rest/service_servers/$', + views.service_servers, + name='service-servers'), + url(r'^rest/ouverture_ports/$', + views.ouverture_ports, + name='ouverture-ports'), url(r'index_portlist/$', views.index_portlist, name='index-portlist'), - url(r'^edit_portlist/(?P[0-9]+)$', views.edit_portlist, name='edit-portlist'), - url(r'^del_portlist/(?P[0-9]+)$', views.del_portlist, name='del-portlist'), + url(r'^edit_portlist/(?P[0-9]+)$', + views.edit_portlist, + name='edit-portlist'), + url(r'^del_portlist/(?P[0-9]+)$', + views.del_portlist, + name='del-portlist'), url(r'^add_portlist/$', views.add_portlist, name='add-portlist'), - url(r'^port_config/(?P[0-9]+)$', views.configure_ports, name='port-config'), - - ] + url(r'^port_config/(?P[0-9]+)$', + views.configure_ports, + name='port-config'), +] diff --git a/machines/views.py b/machines/views.py index ee23504d..3ea0cd11 100644 --- a/machines/views.py +++ b/machines/views.py @@ -42,7 +42,8 @@ from django.contrib.auth import authenticate, login from django.views.decorators.csrf import csrf_exempt from rest_framework.renderers import JSONRenderer -from machines.serializers import ( FullInterfaceSerializer, +from machines.serializers import ( + FullInterfaceSerializer, InterfaceSerializer, TypeSerializer, DomainSerializer, @@ -134,13 +135,15 @@ from re2o.acl import ( ) from re2o.views import form -def f_type_id( is_type_tt ): + +def f_type_id(is_type_tt): """ The id that will be used in HTML to store the value of the field type. Depends on the fact that type is generate using typeahead or not """ return 'id_Interface-type_hidden' if is_type_tt else 'id_Interface-type' -def generate_ipv4_choices( form ) : + +def generate_ipv4_choices(form): """ Generate the parameter choices for the massive_bootstrap_form tag """ f_ipv4 = form.fields['ipv4'] @@ -148,27 +151,29 @@ def generate_ipv4_choices( form ) : choices = '{"":[{key:"",value:"Choisissez d\'abord un type de machine"},' mtype_id = -1 - for ip in f_ipv4.queryset.annotate(mtype_id=F('ip_type__machinetype__id'))\ - .order_by('mtype_id', 'id') : - if mtype_id != ip.mtype_id : + for ip in (f_ipv4.queryset + .annotate(mtype_id=F('ip_type__machinetype__id')) + .order_by('mtype_id', 'id')): + if mtype_id != ip.mtype_id: mtype_id = ip.mtype_id used_mtype_id.append(mtype_id) choices += '],"{t}":[{{key:"",value:"{v}"}},'.format( - t = mtype_id, - v = f_ipv4.empty_label or '""' + t=mtype_id, + v=f_ipv4.empty_label or '""' ) choices += '{{key:{k},value:"{v}"}},'.format( - k = ip.id, - v = ip.ipv4 + k=ip.id, + v=ip.ipv4 ) - for t in form.fields['type'].queryset.exclude(id__in=used_mtype_id) : + for t in form.fields['type'].queryset.exclude(id__in=used_mtype_id): choices += '], "'+str(t.id)+'": [' choices += '{key: "", value: "' + str(f_ipv4.empty_label) + '"},' choices += ']}' return choices -def generate_ipv4_engine( is_type_tt ) : + +def generate_ipv4_engine(is_type_tt): """ Generate the parameter engine for the massive_bootstrap_form tag """ return ( @@ -179,10 +184,11 @@ def generate_ipv4_engine( is_type_tt ) : 'identify: function( obj ) {{ return obj.key; }}' '}} )' ).format( - type_id = f_type_id( is_type_tt ) + type_id=f_type_id(is_type_tt) ) -def generate_ipv4_match_func( is_type_tt ) : + +def generate_ipv4_match_func(is_type_tt): """ Generate the parameter match_func for the massive_bootstrap_form tag """ return ( @@ -196,17 +202,18 @@ def generate_ipv4_match_func( is_type_tt ) : '}}' '}}' ).format( - type_id = f_type_id( is_type_tt ) + type_id=f_type_id(is_type_tt) ) -def generate_ipv4_mbf_param( form, is_type_tt ): + +def generate_ipv4_mbf_param(form, is_type_tt): """ Generate all the parameters to use with the massive_bootstrap_form tag """ - i_choices = { 'ipv4': generate_ipv4_choices( form ) } - i_engine = { 'ipv4': generate_ipv4_engine( is_type_tt ) } - i_match_func = { 'ipv4': generate_ipv4_match_func( is_type_tt ) } - i_update_on = { 'ipv4': [f_type_id( is_type_tt )] } - i_gen_select = { 'ipv4': False } + i_choices = {'ipv4': generate_ipv4_choices(form)} + i_engine = {'ipv4': generate_ipv4_engine(is_type_tt)} + i_match_func = {'ipv4': generate_ipv4_match_func(is_type_tt)} + i_update_on = {'ipv4': [f_type_id(is_type_tt)]} + i_gen_select = {'ipv4': False} i_mbf_param = { 'choices': i_choices, 'engine': i_engine, @@ -216,6 +223,7 @@ def generate_ipv4_mbf_param( form, is_type_tt ): } return i_mbf_param + @login_required @can_create(Machine) @can_edit(User) @@ -245,7 +253,7 @@ def new_machine(request, user, userid): messages.success(request, "La machine a été créée") return redirect(reverse( 'users:profil', - kwargs={'userid':str(user.id)} + kwargs={'userid': str(user.id)} )) i_mbf_param = generate_ipv4_mbf_param(interface, False) return form( @@ -254,26 +262,37 @@ def new_machine(request, user, userid): 'interfaceform': interface, 'domainform': domain, 'i_mbf_param': i_mbf_param, - 'action_name' : 'Créer une machine' + 'action_name': 'Créer une machine' }, 'machines/machine.html', request ) + @login_required @can_edit(Interface) def edit_interface(request, interface_instance, interfaceid): - """ Edition d'une interface. Distingue suivant les droits les valeurs de interfaces et machines que l'user peut modifier - infra permet de modifier le propriétaire""" + """ Edition d'une interface. Distingue suivant les droits les valeurs + de interfaces et machines que l'user peut modifier infra permet de + modifier le propriétaire""" machine_form = EditMachineForm( request.POST or None, instance=interface_instance.machine, user=request.user ) - interface_form = EditInterfaceForm(request.POST or None, instance=interface_instance, user=request.user) - domain_form = DomainForm(request.POST or None, instance=interface_instance.domain) - if machine_form.is_valid() and interface_form.is_valid() and domain_form.is_valid(): + interface_form = EditInterfaceForm( + request.POST or None, + instance=interface_instance, + user=request.user + ) + domain_form = DomainForm( + request.POST or None, + instance=interface_instance.domain + ) + if (machine_form.is_valid() and + interface_form.is_valid() and + domain_form.is_valid()): new_machine = machine_form.save(commit=False) new_interface = interface_form.save(commit=False) new_domain = domain_form.save(commit=False) @@ -286,16 +305,21 @@ def edit_interface(request, interface_instance, interfaceid): messages.success(request, "La machine a été modifiée") return redirect(reverse( 'users:profil', - kwargs={'userid':str(interface_instance.machine.user.id)} - )) - i_mbf_param = generate_ipv4_mbf_param( interface_form, False ) - return form({ - 'machineform': machine_form, - 'interfaceform': interface_form, - 'domainform': domain_form, - 'i_mbf_param': i_mbf_param, - 'action_name' : 'Editer une interface' - }, 'machines/machine.html', request) + kwargs={'userid': str(interface_instance.machine.user.id)} + )) + i_mbf_param = generate_ipv4_mbf_param(interface_form, False) + return form( + { + 'machineform': machine_form, + 'interfaceform': interface_form, + 'domainform': domain_form, + 'i_mbf_param': i_mbf_param, + 'action_name': 'Editer une interface' + }, + 'machines/machine.html', + request + ) + @login_required @can_delete(Machine) @@ -306,9 +330,14 @@ def del_machine(request, machine, machineid): messages.success(request, "La machine a été détruite") return redirect(reverse( 'users:profil', - kwargs={'userid':str(machine.user.id)} - )) - return form({'objet': machine, 'objet_name': 'machine'}, 'machines/delete.html', request) + kwargs={'userid': str(machine.user.id)} + )) + return form( + {'objet': machine, 'objet_name': 'machine'}, + 'machines/delete.html', + request + ) + @login_required @can_create(Interface) @@ -330,15 +359,20 @@ def new_interface(request, machine, machineid): messages.success(request, "L'interface a été ajoutée") return redirect(reverse( 'users:profil', - kwargs={'userid':str(machine.user.id)} - )) - i_mbf_param = generate_ipv4_mbf_param( interface_form, False ) - return form({ - 'interfaceform': interface_form, - 'domainform': domain_form, - 'i_mbf_param': i_mbf_param, - 'action_name' : 'Créer une interface' - }, 'machines/machine.html', request) + kwargs={'userid': str(machine.user.id)} + )) + i_mbf_param = generate_ipv4_mbf_param(interface_form, False) + return form( + { + 'interfaceform': interface_form, + 'domainform': domain_form, + 'i_mbf_param': i_mbf_param, + 'action_name': 'Créer une interface' + }, + 'machines/machine.html', + request + ) + @login_required @can_delete(Interface) @@ -352,9 +386,14 @@ def del_interface(request, interface, interfaceid): messages.success(request, "L'interface a été détruite") return redirect(reverse( 'users:profil', - kwargs={'userid':str(request.user.id)} - )) - return form({'objet': interface, 'objet_name': 'interface'}, 'machines/delete.html', request) + kwargs={'userid': str(request.user.id)} + )) + return form( + {'objet': interface, 'objet_name': 'interface'}, + 'machines/delete.html', + request + ) + @login_required @can_create(Ipv6List) @@ -362,30 +401,48 @@ def del_interface(request, interface, interfaceid): def new_ipv6list(request, interface, interfaceid): """Nouvelle ipv6""" ipv6list_instance = Ipv6List(interface=interface) - ipv6 = Ipv6ListForm(request.POST or None, instance=ipv6list_instance, user=request.user) + ipv6 = Ipv6ListForm( + request.POST or None, + instance=ipv6list_instance, + user=request.user + ) if ipv6.is_valid(): ipv6.save() messages.success(request, "Ipv6 ajoutée") return redirect(reverse( 'machines:index-ipv6', - kwargs={'interfaceid':str(interface.id)} - )) - return form({'ipv6form': ipv6, 'action_name' : 'Créer'}, 'machines/machine.html', request) + kwargs={'interfaceid': str(interface.id)} + )) + return form( + {'ipv6form': ipv6, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(Ipv6List) def edit_ipv6list(request, ipv6list_instance, ipv6listid): """Edition d'une ipv6""" - ipv6 = Ipv6ListForm(request.POST or None, instance=ipv6list_instance, user=request.user) + ipv6 = Ipv6ListForm( + request.POST or None, + instance=ipv6list_instance, + user=request.user + ) if ipv6.is_valid(): if ipv6.changed_data: ipv6.save() messages.success(request, "Ipv6 modifiée") return redirect(reverse( 'machines:index-ipv6', - kwargs={'interfaceid':str(ipv6list_instance.interface.id)} - )) - return form({'ipv6form': ipv6, 'action_name' : 'Editer'}, 'machines/machine.html', request) + kwargs={'interfaceid': str(ipv6list_instance.interface.id)} + )) + return form( + {'ipv6form': ipv6, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete(Ipv6List) @@ -397,26 +454,38 @@ def del_ipv6list(request, ipv6list, ipv6listid): messages.success(request, "L'ipv6 a été détruite") return redirect(reverse( 'machines:index-ipv6', - kwargs={'interfaceid':str(interfaceid)} - )) - return form({'objet': ipv6list, 'objet_name': 'ipv6'}, 'machines/delete.html', request) + kwargs={'interfaceid': str(interfaceid)} + )) + return form( + {'objet': ipv6list, 'objet_name': 'ipv6'}, + 'machines/delete.html', + request + ) + @login_required @can_create(IpType) def add_iptype(request): - """ Ajoute un range d'ip. Intelligence dans le models, fonction views minimaliste""" + """ Ajoute un range d'ip. Intelligence dans le models, fonction views + minimaliste""" iptype = IpTypeForm(request.POST or None) if iptype.is_valid(): iptype.save() messages.success(request, "Ce type d'ip a été ajouté") return redirect(reverse('machines:index-iptype')) - return form({'iptypeform': iptype, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'iptypeform': iptype, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(IpType) def edit_iptype(request, iptype_instance, iptypeid): - """ Edition d'un range. Ne permet pas de le redimensionner pour éviter l'incohérence""" + """ Edition d'un range. Ne permet pas de le redimensionner pour éviter + l'incohérence""" iptype = EditIpTypeForm(request.POST or None, instance=iptype_instance) if iptype.is_valid(): @@ -424,7 +493,12 @@ def edit_iptype(request, iptype_instance, iptypeid): iptype.save() messages.success(request, "Type d'ip modifié") return redirect(reverse('machines:index-iptype')) - return form({'iptypeform': iptype, 'action_name' : 'Editer'}, 'machines/machine.html', request) + return form( + {'iptypeform': iptype, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete_set(IpType) @@ -438,31 +512,52 @@ def del_iptype(request, instances): iptype_del.delete() messages.success(request, "Le type d'ip a été supprimé") except ProtectedError: - messages.error(request, "Le type d'ip %s est affectée à au moins une machine, vous ne pouvez pas le supprimer" % iptype_del) + messages.error( + request, + ("Le type d'ip %s est affectée à au moins une machine, " + "vous ne pouvez pas le supprimer" % iptype_del) + ) return redirect(reverse('machines:index-iptype')) - return form({'iptypeform': iptype, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + return form( + {'iptypeform': iptype, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + @login_required @can_create(MachineType) def add_machinetype(request): - machinetype = MachineTypeForm(request.POST or None) if machinetype.is_valid(): machinetype.save() messages.success(request, "Ce type de machine a été ajouté") return redirect(reverse('machines:index-machinetype')) - return form({'machinetypeform': machinetype, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'machinetypeform': machinetype, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(MachineType) def edit_machinetype(request, machinetype_instance, machinetypeid): - machinetype = MachineTypeForm(request.POST or None, instance=machinetype_instance) + machinetype = MachineTypeForm( + request.POST or None, + instance=machinetype_instance + ) if machinetype.is_valid(): if machinetype.changed_data: machinetype.save() messages.success(request, "Type de machine modifié") return redirect(reverse('machines:index-machinetype')) - return form({'machinetypeform': machinetype, 'action_name' : 'Editer'}, 'machines/machine.html', request) + return form( + {'machinetypeform': machinetype, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete_set(MachineType) @@ -475,9 +570,19 @@ def del_machinetype(request, instances): machinetype_del.delete() messages.success(request, "Le type de machine a été supprimé") except ProtectedError: - messages.error(request, "Le type de machine %s est affectée à au moins une machine, vous ne pouvez pas le supprimer" % machinetype_del) + messages.error( + request, + ("Le type de machine %s est affectée à au moins une " + "machine, vous ne pouvez pas le supprimer" + % machinetype_del) + ) return redirect(reverse('machines:index-machinetype')) - return form({'machinetypeform': machinetype, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + return form( + {'machinetypeform': machinetype, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + @login_required @can_create(Extension) @@ -487,18 +592,31 @@ def add_extension(request): extension.save() messages.success(request, "Cette extension a été ajoutée") return redirect(reverse('machines:index-extension')) - return form({'extensionform': extension, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'extensionform': extension, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(Extension) def edit_extension(request, extension_instance, extensionid): - extension = ExtensionForm(request.POST or None, instance=extension_instance) + extension = ExtensionForm( + request.POST or None, + instance=extension_instance + ) if extension.is_valid(): if extension.changed_data: extension.save() messages.success(request, "Extension modifiée") return redirect(reverse('machines:index-extension')) - return form({'extensionform': extension, 'action_name' : 'Editer'}, 'machines/machine.html', request) + return form( + {'extensionform': extension, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete_set(Extension) @@ -511,9 +629,19 @@ def del_extension(request, instances): extension_del.delete() messages.success(request, "L'extension a été supprimée") except ProtectedError: - messages.error(request, "L'extension %s est affectée à au moins un type de machine, vous ne pouvez pas la supprimer" % extension_del) + messages.error( + request, + ("L'extension %s est affectée à au moins un type de " + "machine, vous ne pouvez pas la supprimer" + % extension_del) + ) return redirect(reverse('machines:index-extension')) - return form({'extensionform': extension, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + return form( + {'extensionform': extension, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + @login_required @can_create(SOA) @@ -523,7 +651,12 @@ def add_soa(request): soa.save() messages.success(request, "Cet enregistrement SOA a été ajouté") return redirect(reverse('machines:index-extension')) - return form({'soaform': soa, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'soaform': soa, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(SOA) @@ -534,7 +667,12 @@ def edit_soa(request, soa_instance, soaid): soa.save() messages.success(request, "SOA modifié") return redirect(reverse('machines:index-extension')) - return form({'soaform': soa, 'action_name' : 'Editer'}, 'machines/machine.html', request) + return form( + {'soaform': soa, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete_set(SOA) @@ -547,9 +685,18 @@ def del_soa(request, instances): soa_del.delete() messages.success(request, "Le SOA a été supprimée") except ProtectedError: - messages.error(request, "Erreur le SOA suivant %s ne peut être supprimé" % soa_del) + messages.error( + request, + ("Erreur le SOA suivant %s ne peut être supprimé" + % soa_del) + ) return redirect(reverse('machines:index-extension')) - return form({'soaform': soa, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + return form( + {'soaform': soa, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + @login_required @can_create(Mx) @@ -559,7 +706,12 @@ def add_mx(request): mx.save() messages.success(request, "Cet enregistrement mx a été ajouté") return redirect(reverse('machines:index-extension')) - return form({'mxform': mx, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'mxform': mx, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(Mx) @@ -570,7 +722,12 @@ def edit_mx(request, mx_instance, mxid): mx.save() messages.success(request, "Mx modifié") return redirect(reverse('machines:index-extension')) - return form({'mxform': mx, 'action_name' : 'Editer'}, 'machines/machine.html', request) + return form( + {'mxform': mx, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete_set(Mx) @@ -583,9 +740,18 @@ def del_mx(request, instances): mx_del.delete() messages.success(request, "L'mx a été supprimée") except ProtectedError: - messages.error(request, "Erreur le Mx suivant %s ne peut être supprimé" % mx_del) + messages.error( + request, + ("Erreur le Mx suivant %s ne peut être supprimé" + % mx_del) + ) return redirect(reverse('machines:index-extension')) - return form({'mxform': mx, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + return form( + {'mxform': mx, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + @login_required @can_create(Ns) @@ -595,7 +761,12 @@ def add_ns(request): ns.save() messages.success(request, "Cet enregistrement ns a été ajouté") return redirect(reverse('machines:index-extension')) - return form({'nsform': ns, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'nsform': ns, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(Ns) @@ -606,7 +777,12 @@ def edit_ns(request, ns_instance, nsid): ns.save() messages.success(request, "Ns modifié") return redirect(reverse('machines:index-extension')) - return form({'nsform': ns, 'action_name' : 'Editer'}, 'machines/machine.html', request) + return form( + {'nsform': ns, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete_set(Ns) @@ -619,9 +795,18 @@ def del_ns(request, instances): ns_del.delete() messages.success(request, "Le ns a été supprimée") except ProtectedError: - messages.error(request, "Erreur le Ns suivant %s ne peut être supprimé" % ns_del) + messages.error( + request, + ("Erreur le Ns suivant %s ne peut être supprimé" + % ns_del) + ) return redirect(reverse('machines:index-extension')) - return form({'nsform': ns, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + return form( + {'nsform': ns, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + @login_required @can_create(Txt) @@ -631,7 +816,12 @@ def add_txt(request): txt.save() messages.success(request, "Cet enregistrement text a été ajouté") return redirect(reverse('machines:index-extension')) - return form({'txtform': txt, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'txtform': txt, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(Txt) @@ -642,7 +832,12 @@ def edit_txt(request, txt_instance, txtid): txt.save() messages.success(request, "Txt modifié") return redirect(reverse('machines:index-extension')) - return form({'txtform': txt, 'action_name' : 'Editer'}, 'machines/machine.html', request) + return form( + {'txtform': txt, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete_set(Txt) @@ -655,9 +850,18 @@ def del_txt(request, instances): txt_del.delete() messages.success(request, "Le txt a été supprimé") except ProtectedError: - messages.error(request, "Erreur le Txt suivant %s ne peut être supprimé" % txt_del) + messages.error( + request, + ("Erreur le Txt suivant %s ne peut être supprimé" + % txt_del) + ) return redirect(reverse('machines:index-extension')) - return form({'txtform': txt, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + return form( + {'txtform': txt, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + @login_required @can_create(Srv) @@ -667,7 +871,12 @@ def add_srv(request): srv.save() messages.success(request, "Cet enregistrement srv a été ajouté") return redirect(reverse('machines:index-extension')) - return form({'srvform': srv, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'srvform': srv, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(Srv) @@ -678,7 +887,12 @@ def edit_srv(request, srv_instance, srvid): srv.save() messages.success(request, "Srv modifié") return redirect(reverse('machines:index-extension')) - return form({'srvform': srv, 'action_name' : 'Editer'}, 'machines/machine.html', request) + return form( + {'srvform': srv, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete_set(Srv) @@ -691,9 +905,18 @@ def del_srv(request, instances): srv_del.delete() messages.success(request, "L'srv a été supprimée") except ProtectedError: - messages.error(request, "Erreur le Srv suivant %s ne peut être supprimé" % srv_del) + messages.error( + request, + ("Erreur le Srv suivant %s ne peut être supprimé" + % srv_del) + ) return redirect(reverse('machines:index-extension')) - return form({'srvform': srv, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + return form( + {'srvform': srv, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + @login_required @can_create(Domain) @@ -707,23 +930,39 @@ def add_alias(request, interface, interfaceid): messages.success(request, "Cet alias a été ajouté") return redirect(reverse( 'machines:index-alias', - kwargs={'interfaceid':str(interfaceid)} - )) - return form({'aliasform': alias, 'action_name' : 'Créer'}, 'machines/machine.html', request) + kwargs={'interfaceid': str(interfaceid)} + )) + return form( + {'aliasform': alias, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(Domain) def edit_alias(request, domain_instance, domainid): - alias = AliasForm(request.POST or None, instance=domain_instance, user=request.user) + alias = AliasForm( + request.POST or None, + instance=domain_instance, + user=request.user + ) if alias.is_valid(): if alias.changed_data: domain_instance = alias.save() messages.success(request, "Alias modifié") return redirect(reverse( 'machines:index-alias', - kwargs={'interfaceid':str(domain_instance.cname.interface_parent.id)} - )) - return form({'aliasform': alias, 'action_name' : 'Editer'}, 'machines/machine.html', request) + kwargs={ + 'interfaceid': str(domain_instance.cname.interface_parent.id) + } + )) + return form( + {'aliasform': alias, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(Interface) @@ -734,14 +973,25 @@ def del_alias(request, interface, interfaceid): for alias_del in alias_dels: try: alias_del.delete() - messages.success(request, "L'alias %s a été supprimé" % alias_del) + messages.success( + request, + "L'alias %s a été supprimé" % alias_del + ) except ProtectedError: - messages.error(request, "Erreur l'alias suivant %s ne peut être supprimé" % alias_del) + messages.error( + request, + ("Erreur l'alias suivant %s ne peut être supprimé" + % alias_del) + ) return redirect(reverse( 'machines:index-alias', - kwargs={'interfaceid':str(interfaceid)} - )) - return form({'aliasform': alias, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + kwargs={'interfaceid': str(interfaceid)} + )) + return form( + {'aliasform': alias, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) @login_required @@ -752,7 +1002,12 @@ def add_service(request): service.save() messages.success(request, "Cet enregistrement service a été ajouté") return redirect(reverse('machines:index-service')) - return form({'serviceform': service, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'serviceform': service, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(Service) @@ -763,7 +1018,12 @@ def edit_service(request, service_instance, serviceid): service.save() messages.success(request, "Service modifié") return redirect(reverse('machines:index-service')) - return form({'serviceform': service, 'action_name' : 'Editer'}, 'machines/machine.html', request) + return form( + {'serviceform': service, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete_set(Service) @@ -776,9 +1036,18 @@ def del_service(request, instances): service_del.delete() messages.success(request, "Le service a été supprimée") except ProtectedError: - messages.error(request, "Erreur le service suivant %s ne peut être supprimé" % service_del) + messages.error( + request, + ("Erreur le service suivant %s ne peut être supprimé" + % service_del) + ) return redirect(reverse('machines:index-service')) - return form({'serviceform': service, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + return form( + {'serviceform': service, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + @login_required @can_create(Vlan) @@ -788,7 +1057,12 @@ def add_vlan(request): vlan.save() messages.success(request, "Cet enregistrement vlan a été ajouté") return redirect(reverse('machines:index-vlan')) - return form({'vlanform': vlan, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'vlanform': vlan, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(Vlan) @@ -799,7 +1073,12 @@ def edit_vlan(request, vlan_instance, vlanid): vlan.save() messages.success(request, "Vlan modifié") return redirect(reverse('machines:index-vlan')) - return form({'vlanform': vlan, 'action_name' : 'Editer'}, 'machines/machine.html', request) + return form( + {'vlanform': vlan, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete_set(Vlan) @@ -812,9 +1091,18 @@ def del_vlan(request, instances): vlan_del.delete() messages.success(request, "Le vlan a été supprimée") except ProtectedError: - messages.error(request, "Erreur le Vlan suivant %s ne peut être supprimé" % vlan_del) + messages.error( + request, + ("Erreur le Vlan suivant %s ne peut être supprimé" + % vlan_del) + ) return redirect(reverse('machines:index-vlan')) - return form({'vlanform': vlan, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + return form( + {'vlanform': vlan, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + @login_required @can_create(Nas) @@ -824,7 +1112,12 @@ def add_nas(request): nas.save() messages.success(request, "Cet enregistrement nas a été ajouté") return redirect(reverse('machines:index-nas')) - return form({'nasform': nas, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'nasform': nas, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_edit(Nas) @@ -835,7 +1128,12 @@ def edit_nas(request, nas_instance, nasid): nas.save() messages.success(request, "Nas modifié") return redirect(reverse('machines:index-nas')) - return form({'nasform': nas, 'action_name' : 'Editer'}, 'machines/machine.html', request) + return form( + {'nasform': nas, 'action_name': 'Editer'}, + 'machines/machine.html', + request + ) + @login_required @can_delete_set(Nas) @@ -848,47 +1146,98 @@ def del_nas(request, instances): nas_del.delete() messages.success(request, "Le nas a été supprimé") except ProtectedError: - messages.error(request, "Erreur le Nas suivant %s ne peut être supprimé" % nas_del) + messages.error( + request, + ("Erreur le Nas suivant %s ne peut être supprimé" + % nas_del) + ) return redirect(reverse('machines:index-nas')) - return form({'nasform': nas, 'action_name' : 'Supprimer'}, 'machines/machine.html', request) + return form( + {'nasform': nas, 'action_name': 'Supprimer'}, + 'machines/machine.html', + request + ) + @login_required @can_view_all(Machine) def index(request): - pagination_large_number = GeneralOption.get_cached_value('pagination_large_number') - machines_list = Machine.objects.select_related('user').prefetch_related('interface_set__domain__extension').prefetch_related('interface_set__ipv4__ip_type').prefetch_related('interface_set__type__ip_type__extension').prefetch_related('interface_set__domain__related_domain__extension').prefetch_related('interface_set__ipv6list') + pagination_large_number = (GeneralOption + .get_cached_value('pagination_large_number')) + machines_list = (Machine.objects + .select_related('user') + .prefetch_related('interface_set__domain__extension') + .prefetch_related('interface_set__ipv4__ip_type') + .prefetch_related( + 'interface_set__type__ip_type__extension' + ).prefetch_related( + 'interface_set__domain__related_domain__extension' + ).prefetch_related('interface_set__ipv6list')) machines_list = SortTable.sort( machines_list, request.GET.get('col'), request.GET.get('order'), SortTable.MACHINES_INDEX ) - machines_list = re2o_paginator(request, machines_list, pagination_large_number) - return render(request, 'machines/index.html', {'machines_list': machines_list}) + machines_list = re2o_paginator( + request, + machines_list, + pagination_large_number + ) + return render( + request, + 'machines/index.html', + {'machines_list': machines_list} + ) + @login_required @can_view_all(IpType) def index_iptype(request): - iptype_list = IpType.objects.select_related('extension').select_related('vlan').order_by('type') - return render(request, 'machines/index_iptype.html', {'iptype_list':iptype_list}) + iptype_list = (IpType.objects + .select_related('extension') + .select_related('vlan') + .order_by('type')) + return render( + request, + 'machines/index_iptype.html', + {'iptype_list': iptype_list} + ) + @login_required @can_view_all(Vlan) def index_vlan(request): vlan_list = Vlan.objects.prefetch_related('iptype_set').order_by('vlan_id') - return render(request, 'machines/index_vlan.html', {'vlan_list':vlan_list}) + return render( + request, + 'machines/index_vlan.html', + {'vlan_list': vlan_list} + ) + @login_required @can_view_all(MachineType) def index_machinetype(request): - machinetype_list = MachineType.objects.select_related('ip_type').order_by('type') - return render(request, 'machines/index_machinetype.html', {'machinetype_list':machinetype_list}) + machinetype_list = (MachineType.objects + .select_related('ip_type') + .order_by('type')) + return render( + request, + 'machines/index_machinetype.html', + {'machinetype_list': machinetype_list} + ) + @login_required @can_view_all(Nas) def index_nas(request): - nas_list = Nas.objects.select_related('machine_type').select_related('nas_type').order_by('name') - return render(request, 'machines/index_nas.html', {'nas_list':nas_list}) + nas_list = (Nas.objects + .select_related('machine_type') + .select_related('nas_type') + .order_by('name')) + return render(request, 'machines/index_nas.html', {'nas_list': nas_list}) + @login_required @can_view_all(SOA) @@ -898,54 +1247,113 @@ def index_nas(request): @can_view_all(Srv) @can_view_all(Extension) def index_extension(request): - extension_list = Extension.objects.select_related('origin').select_related('soa').order_by('name') + extension_list = (Extension.objects + .select_related('origin') + .select_related('soa') + .order_by('name')) soa_list = SOA.objects.order_by('name') - mx_list = Mx.objects.order_by('zone').select_related('zone').select_related('name__extension') - ns_list = Ns.objects.order_by('zone').select_related('zone').select_related('ns__extension') + mx_list = (Mx.objects + .order_by('zone') + .select_related('zone') + .select_related('name__extension')) + ns_list = (Ns.objects + .order_by('zone') + .select_related('zone') + .select_related('ns__extension')) txt_list = Txt.objects.all().select_related('zone') - srv_list = Srv.objects.all().select_related('extension').select_related('target__extension') - return render(request, 'machines/index_extension.html', {'extension_list':extension_list, 'soa_list': soa_list, 'mx_list': mx_list, 'ns_list': ns_list, 'txt_list' : txt_list, 'srv_list': srv_list}) + srv_list = (Srv.objects + .all() + .select_related('extension') + .select_related('target__extension')) + return render( + request, + 'machines/index_extension.html', + { + 'extension_list': extension_list, + 'soa_list': soa_list, + 'mx_list': mx_list, + 'ns_list': ns_list, + 'txt_list': txt_list, + 'srv_list': srv_list + } + ) + @login_required @can_edit(Interface) def index_alias(request, interface, interfaceid): - alias_list = Domain.objects.filter(cname=Domain.objects.filter(interface_parent=interface)).order_by('name') - return render(request, 'machines/index_alias.html', {'alias_list':alias_list, 'interface_id': interfaceid}) + alias_list = Domain.objects.filter( + cname=Domain.objects.filter(interface_parent=interface) + ).order_by('name') + return render( + request, + 'machines/index_alias.html', + {'alias_list': alias_list, 'interface_id': interfaceid} + ) + @login_required @can_edit(Interface) def index_ipv6(request, interface, interfaceid): ipv6_list = Ipv6List.objects.filter(interface=interface) - return render(request, 'machines/index_ipv6.html', {'ipv6_list':ipv6_list, 'interface_id': interfaceid}) + return render( + request, + 'machines/index_ipv6.html', + {'ipv6_list': ipv6_list, 'interface_id': interfaceid} + ) + @login_required @can_view_all(Service) def index_service(request): - service_list = Service.objects.prefetch_related('service_link_set__server__domain__extension').all() - servers_list = Service_link.objects.select_related('server__domain__extension').select_related('service').all() - return render(request, 'machines/index_service.html', {'service_list':service_list, 'servers_list':servers_list}) + service_list = (Service.objects + .prefetch_related( + 'service_link_set__server__domain__extension' + ).all()) + servers_list = (Service_link.objects + .select_related('server__domain__extension') + .select_related('service') + .all()) + return render( + request, + 'machines/index_service.html', + {'service_list': service_list, 'servers_list': servers_list} + ) @login_required @can_view_all(OuverturePortList) def index_portlist(request): - port_list = OuverturePortList.objects.prefetch_related('ouvertureport_set')\ - .prefetch_related('interface_set__domain__extension')\ - .prefetch_related('interface_set__machine__user').order_by('name') - return render(request, "machines/index_portlist.html", {'port_list':port_list}) + port_list = (OuverturePortList.objects + .prefetch_related('ouvertureport_set') + .prefetch_related('interface_set__domain__extension') + .prefetch_related('interface_set__machine__user') + .order_by('name')) + return render( + request, + "machines/index_portlist.html", + {'port_list': port_list} + ) + @login_required @can_edit(OuverturePortList) def edit_portlist(request, ouvertureportlist_instance, ouvertureportlistid): - port_list = EditOuverturePortListForm(request.POST or None, instance=ouvertureportlist_instance) + port_list = EditOuverturePortListForm( + request.POST or None, + instance=ouvertureportlist_instance + ) port_formset = modelformset_factory( - OuverturePort, - fields=('begin','end','protocole','io'), - extra=0, - can_delete=True, - min_num=1, - validate_min=True, - )(request.POST or None, queryset=ouvertureportlist_instance.ouvertureport_set.all()) + OuverturePort, + fields=('begin', 'end', 'protocole', 'io'), + extra=0, + can_delete=True, + min_num=1, + validate_min=True, + )( + request.POST or None, + queryset=ouvertureportlist_instance.ouvertureport_set.all() + ) if port_list.is_valid() and port_formset.is_valid(): if port_list.changed_data: pl = port_list.save() @@ -959,7 +1367,12 @@ def edit_portlist(request, ouvertureportlist_instance, ouvertureportlistid): port.save() messages.success(request, "Liste de ports modifiée") return redirect(reverse('machines:index-portlist')) - return form({'port_list' : port_list, 'ports' : port_formset}, 'machines/edit_portlist.html', request) + return form( + {'port_list': port_list, 'ports': port_formset}, + 'machines/edit_portlist.html', + request + ) + @login_required @can_delete(OuverturePortList) @@ -968,17 +1381,18 @@ def del_portlist(request, port_list_instance, ouvertureportlistid): messages.success(request, "La liste de ports a été supprimée") return redirect(reverse('machines:index-portlist')) + @login_required @can_create(OuverturePortList) def add_portlist(request): port_list = EditOuverturePortListForm(request.POST or None) port_formset = modelformset_factory( OuverturePort, - fields=('begin','end','protocole','io'), + fields=('begin', 'end', 'protocole', 'io'), extra=0, can_delete=True, - min_num=1, - validate_min=True, + min_num=1, + validate_min=True, )(request.POST or None, queryset=OuverturePort.objects.none()) if port_list.is_valid() and port_formset.is_valid(): pl = port_list.save() @@ -990,36 +1404,59 @@ def add_portlist(request): port.save() messages.success(request, "Liste de ports créée") return redirect(reverse('machines:index-portlist')) - return form({'port_list' : port_list, 'ports' : port_formset}, 'machines/edit_portlist.html', request) + return form( + {'port_list': port_list, 'ports': port_formset}, + 'machines/edit_portlist.html', + request + ) port_list = EditOuverturePortListForm(request.POST or None) if port_list.is_valid(): port_list.save() messages.success(request, "Liste de ports créée") return redirect(reverse('machines:index-portlist')) - return form({'machineform' : port_list, 'action_name' : 'Créer'}, 'machines/machine.html', request) + return form( + {'machineform': port_list, 'action_name': 'Créer'}, + 'machines/machine.html', + request + ) + @login_required @can_create(OuverturePort) @can_edit(Interface) def configure_ports(request, interface_instance, interfaceid): if not interface_instance.may_have_port_open(): - messages.error(request, "Attention, l'ipv4 n'est pas publique, l'ouverture n'aura pas d'effet en v4") - interface = EditOuverturePortConfigForm(request.POST or None, instance=interface_instance) + messages.error( + request, + ("Attention, l'ipv4 n'est pas publique, l'ouverture n'aura pas " + "d'effet en v4") + ) + interface = EditOuverturePortConfigForm( + request.POST or None, + instance=interface_instance + ) if interface.is_valid(): if interface.changed_data: interface.save() messages.success(request, "Configuration des ports mise à jour.") return redirect(reverse('machines:index')) - return form({'interfaceform' : interface, 'action_name' : 'Editer la configuration'}, 'machines/machine.html', request) + return form( + {'interfaceform': interface, 'action_name': 'Editer la configuration'}, + 'machines/machine.html', + request + ) + """ Framework Rest """ + class JSONResponse(HttpResponse): def __init__(self, data, **kwargs): content = JSONRenderer().render(data) kwargs['content_type'] = 'application/json' super(JSONResponse, self).__init__(content, **kwargs) + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -1028,6 +1465,7 @@ def mac_ip_list(request): seria = InterfaceSerializer(interfaces, many=True) return seria.data + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -1036,14 +1474,23 @@ def full_mac_ip_list(request): seria = FullInterfaceSerializer(interfaces, many=True) return seria.data + @csrf_exempt @login_required @permission_required('machines.serveur') def alias(request): - alias = Domain.objects.filter(interface_parent=None).filter(cname__in=Domain.objects.filter(interface_parent__in=Interface.objects.exclude(ipv4=None))).select_related('extension').select_related('cname__extension') + alias = (Domain.objects + .filter(interface_parent=None) + .filter( + cname__in=Domain.objects.filter( + interface_parent__in=Interface.objects.exclude(ipv4=None) + ) + ).select_related('extension') + .select_related('cname__extension')) seria = DomainSerializer(alias, many=True) return JSONResponse(seria.data) + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -1052,14 +1499,18 @@ def corresp(request): seria = TypeSerializer(type, many=True) return JSONResponse(seria.data) + @csrf_exempt @login_required @permission_required('machines.serveur') def mx(request): - mx = Mx.objects.all().select_related('zone').select_related('name__extension') + mx = (Mx.objects.all() + .select_related('zone') + .select_related('name__extension')) seria = MxSerializer(mx, many=True) return JSONResponse(seria.data) + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -1068,22 +1519,34 @@ def txt(request): seria = TxtSerializer(txt, many=True) return JSONResponse(seria.data) + @csrf_exempt @login_required @permission_required('machines.serveur') def srv(request): - srv = Srv.objects.all().select_related('extension').select_related('target__extension') + srv = (Srv.objects + .all() + .select_related('extension') + .select_related('target__extension')) seria = SrvSerializer(srv, many=True) return JSONResponse(seria.data) + @csrf_exempt @login_required @permission_required('machines.serveur') def ns(request): - ns = Ns.objects.exclude(ns__in=Domain.objects.filter(interface_parent__in=Interface.objects.filter(ipv4=None))).select_related('zone').select_related('ns__extension') + ns = (Ns.objects + .exclude( + ns__in=Domain.objects.filter( + interface_parent__in=Interface.objects.filter(ipv4=None) + ) + ).select_related('zone') + .select_related('ns__extension')) seria = NsSerializer(ns, many=True) return JSONResponse(seria.data) + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -1092,6 +1555,7 @@ def zones(request): seria = ExtensionSerializer(zones, many=True) return JSONResponse(seria.data) + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -1099,6 +1563,7 @@ def mac_ip(request): seria = mac_ip_list(request) return JSONResponse(seria) + @csrf_exempt @login_required @permission_required('machines.serveur') @@ -1106,50 +1571,100 @@ def mac_ip_dns(request): seria = full_mac_ip_list(request) return JSONResponse(seria) + @csrf_exempt @login_required @permission_required('machines.serveur') def service_servers(request): - service_link = Service_link.objects.all().select_related('server__domain').select_related('service') + service_link = (Service_link.objects + .all() + .select_related('server__domain') + .select_related('service')) seria = ServiceServersSerializer(service_link, many=True) return JSONResponse(seria.data) + @csrf_exempt @login_required @permission_required('machines.serveur') def ouverture_ports(request): - r = {'ipv4':{}, 'ipv6':{}} - for o in OuverturePortList.objects.all().prefetch_related('ouvertureport_set').prefetch_related('interface_set', 'interface_set__ipv4'): + r = {'ipv4': {}, 'ipv6': {}} + for o in (OuverturePortList.objects + .all() + .prefetch_related('ouvertureport_set') + .prefetch_related('interface_set', 'interface_set__ipv4')): pl = { - "tcp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.IN))), - "tcp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.OUT))), - "udp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.IN))), - "udp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.OUT))), + "tcp_in": set(map( + str, + o.ouvertureport_set.filter( + protocole=OuverturePort.TCP, + io=OuverturePort.IN + ) + )), + "tcp_out": set(map( + str, + o.ouvertureport_set.filter( + protocole=OuverturePort.TCP, + io=OuverturePort.OUT + ) + )), + "udp_in": set(map( + str, + o.ouvertureport_set.filter( + protocole=OuverturePort.UDP, + io=OuverturePort.IN + ) + )), + "udp_out": set(map( + str, + o.ouvertureport_set.filter( + protocole=OuverturePort.UDP, + io=OuverturePort.OUT + ) + )), } for i in filter_active_interfaces(o.interface_set): if i.may_have_port_open(): d = r['ipv4'].get(i.ipv4.ipv4, {}) - d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"]) - d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"]) - d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"]) - d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"]) + d["tcp_in"] = (d.get("tcp_in", set()) + .union(pl["tcp_in"])) + d["tcp_out"] = (d.get("tcp_out", set()) + .union(pl["tcp_out"])) + d["udp_in"] = (d.get("udp_in", set()) + .union(pl["udp_in"])) + d["udp_out"] = (d.get("udp_out", set()) + .union(pl["udp_out"])) r['ipv4'][i.ipv4.ipv4] = d if i.ipv6(): for ipv6 in i.ipv6(): d = r['ipv6'].get(ipv6.ipv6, {}) - d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"]) - d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"]) - d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"]) - d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"]) + d["tcp_in"] = (d.get("tcp_in", set()) + .union(pl["tcp_in"])) + d["tcp_out"] = (d.get("tcp_out", set()) + .union(pl["tcp_out"])) + d["udp_in"] = (d.get("udp_in", set()) + .union(pl["udp_in"])) + d["udp_out"] = (d.get("udp_out", set()) + .union(pl["udp_out"])) r['ipv6'][ipv6.ipv6] = d return JSONResponse(r) + @csrf_exempt @login_required @permission_required('machines.serveur') def regen_achieved(request): - obj = Service_link.objects.filter(service__in=Service.objects.filter(service_type=request.POST['service']), server__in=Interface.objects.filter(domain__in=Domain.objects.filter(name=request.POST['server']))) + obj = (Service_link.objects + .filter( + service__in=Service.objects.filter( + service_type=request.POST['service'] + ), + server__in=Interface.objects.filter( + domain__in=Domain.objects.filter( + name=request.POST['server'] + ) + ) + )) if obj: obj.first().done_regen() return HttpResponse("Ok") - From 48d904b6de7ee08873d5e85ac910cc518066da22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Fri, 13 Apr 2018 23:42:22 +0000 Subject: [PATCH 06/22] Pep8 compliance on re2o --- preferences/acl.py | 1 + preferences/forms.py | 15 ++- preferences/models.py | 7 +- preferences/urls.py | 2 +- preferences/views.py | 6 +- re2o/__init__.py | 1 - re2o/contributors.py | 27 +++- re2o/field_permissions.py | 5 +- re2o/management/commands/gen_contrib.py | 13 +- re2o/mixins.py | 62 +++++++-- re2o/script_utils.py | 56 ++++---- re2o/settings.py | 2 +- re2o/settings_local.example.py | 17 ++- re2o/templatetags/__init__.py | 1 - re2o/templatetags/acl.py | 134 ++++++++++++-------- re2o/templatetags/massive_bootstrap_form.py | 53 ++------ re2o/templatetags/self_adhesion.py | 1 + re2o/utils.py | 28 ++-- re2o/views.py | 106 +++++++++------- 19 files changed, 313 insertions(+), 224 deletions(-) diff --git a/preferences/acl.py b/preferences/acl.py index 8ffb4c9b..1f3f666e 100644 --- a/preferences/acl.py +++ b/preferences/acl.py @@ -26,6 +26,7 @@ Here are defined some functions to check acl on the application. """ + def can_view(user): """Check if an user can view the application. diff --git a/preferences/forms.py b/preferences/forms.py index a6b8426a..b4a79dd0 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -44,12 +44,16 @@ class EditOptionalUserForm(ModelForm): prefix=prefix, **kwargs ) - self.fields['is_tel_mandatory'].label = 'Exiger un numéro de\ - téléphone' - self.fields['user_solde'].label = 'Activation du solde pour\ - les utilisateurs' + self.fields['is_tel_mandatory'].label = ( + 'Exiger un numéro de téléphone' + ) + self.fields['user_solde'].label = ( + 'Activation du solde pour les utilisateurs' + ) self.fields['max_solde'].label = 'Solde maximum' - self.fields['min_online_payment'].label = 'Montant de rechargement minimum en ligne' + self.fields['min_online_payment'].label = ( + 'Montant de rechargement minimum en ligne' + ) self.fields['self_adhesion'].label = 'Auto inscription' @@ -162,7 +166,6 @@ class EditAssoOptionForm(ModelForm): return cleaned_data - class EditMailMessageOptionForm(ModelForm): """Formulaire d'edition des messages de bienvenue personnalisés""" class Meta: diff --git a/preferences/models.py b/preferences/models.py index c0d761e6..c5363fdd 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -36,6 +36,7 @@ from django.core.cache import cache from .aes_field import AESEncryptedField from re2o.mixins import AclMixin + class PreferencesModel(models.Model): @classmethod def set_in_cache(cls): @@ -46,7 +47,7 @@ class PreferencesModel(models.Model): @classmethod def get_cached_value(cls, key): instance = cache.get(cls().__class__.__name__.lower()) - if instance == None: + if instance is None: instance = cls.set_in_cache() return getattr(instance, key) @@ -146,7 +147,7 @@ class OptionalMachine(AclMixin, PreferencesModel): @cached_property def ipv6(self): - return not self.get_cached_value('ipv6_mode') == 'DISABLED' + return not self.get_cached_value('ipv6_mode') == 'DISABLED' class Meta: permissions = ( @@ -230,7 +231,7 @@ class GeneralOption(AclMixin, PreferencesModel): blank=True, ) GTU = models.FileField( - upload_to = '', + upload_to='', default="", null=True, blank=True, diff --git a/preferences/urls.py b/preferences/urls.py index 3bc15275..d8aba5b9 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -73,7 +73,7 @@ urlpatterns = [ r'^history/(?P\w+)/(?P[0-9]+)$', re2o.views.history, name='history', - kwargs={'application':'preferences'}, + kwargs={'application': 'preferences'}, ), url(r'^$', views.display_options, name='display-options'), ] diff --git a/preferences/views.py b/preferences/views.py index 22341c28..aa065d1d 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -128,7 +128,7 @@ def add_service(request): messages.success(request, "Ce service a été ajouté") return redirect(reverse('preferences:display-options')) return form( - {'preferenceform': service, 'action_name' : 'Ajouter'}, + {'preferenceform': service, 'action_name': 'Ajouter'}, 'preferences/preferences.html', request ) @@ -151,7 +151,7 @@ def edit_service(request, service_instance, serviceid): messages.success(request, "Service modifié") return redirect(reverse('preferences:display-options')) return form( - {'preferenceform': service, 'action_name' : 'Editer'}, + {'preferenceform': service, 'action_name': 'Editer'}, 'preferences/preferences.html', request ) @@ -175,7 +175,7 @@ def del_services(request, instances): suivant %s ne peut être supprimé" % services_del) return redirect(reverse('preferences:display-options')) return form( - {'preferenceform': services, 'action_name' : 'Supprimer'}, + {'preferenceform': services, 'action_name': 'Supprimer'}, 'preferences/preferences.html', request ) diff --git a/re2o/__init__.py b/re2o/__init__.py index fc1be5d7..cd256e09 100644 --- a/re2o/__init__.py +++ b/re2o/__init__.py @@ -20,4 +20,3 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - diff --git a/re2o/contributors.py b/re2o/contributors.py index 00e97e41..e5af0a64 100644 --- a/re2o/contributors.py +++ b/re2o/contributors.py @@ -1,3 +1,28 @@ #!/usr/bin/env python3 -CONTRIBUTORS = ['Gabriel "Chirac" Détraz', 'Maël "MoaMoaK" Kervella', 'Hugo "Klafyvel" Levy--Falk', 'Augustin "Dahlaro" Lemesle', 'Goulven "Lhark" Kermarec', 'Guillaume "Guimoz" Goessel', 'Yoann "Nanoy" Pietri', 'Matthieu "Lebanni" Michelet', 'Arthur "Grizzly" Grisel-Davy', 'Simon "Rezatoune" Brélivet', 'Sellem Lev-Arcady', 'David "5-1" Sinquin', 'Pierre "Redstorm" Cadart', 'Éloi "Goslig" Alain', 'Laouen "Volgarr" Fernet', 'Joanne Steiner', '"Krokmou"', 'Thibault "Tipunchetrhum" de Boutray', 'Baptiste "B" Fournier', 'Daniel "Dstan" Stan', 'Hugo "Shaka" Hervieux', '"Mikachu"', 'Thomas "Nymous" Gaudin', '"Esum"'] +CONTRIBUTORS = [ + 'Gabriel "Chirac" Détraz', + 'Maël "MoaMoaK" Kervella', + 'Hugo "Klafyvel" Levy--Falk', + 'Augustin "Dahlaro" Lemesle', + 'Goulven "Lhark" Kermarec', + 'Guillaume "Guimoz" Goessel', + 'Yoann "Nanoy" Pietri', + 'Matthieu "Lebanni" Michelet', + 'Arthur "Grizzly" Grisel-Davy', + 'Simon "Rezatoune" Brélivet', + 'Sellem Lev-Arcady', + 'David "5-1" Sinquin', + 'Pierre "Redstorm" Cadart', + 'Éloi "Goslig" Alain', + 'Laouen "Volgarr" Fernet', + 'Joanne Steiner', + '"Krokmou"', + 'Thibault "Tipunchetrhum" de Boutray', + 'Baptiste "B" Fournier', + 'Daniel "Dstan" Stan', + 'Hugo "Shaka" Hervieux', + '"Mikachu"', + 'Thomas "Nymous" Gaudin', + '"Esum"' +] diff --git a/re2o/field_permissions.py b/re2o/field_permissions.py index dc5466c4..e4c36552 100644 --- a/re2o/field_permissions.py +++ b/re2o/field_permissions.py @@ -43,13 +43,15 @@ class FieldPermissionModelMixin: if result is not None: return result else: - result = user.has_perm(perm) # Don't supply 'obj', or else infinite recursion. + # Don't supply 'obj', or else infinite recursion. + result = user.has_perm(perm) if result: return True # If no requirement can be met, then permission is denied. return False + class FieldPermissionModel(FieldPermissionModelMixin, models.Model): class Meta: abstract = True @@ -76,4 +78,3 @@ class FieldPermissionFormMixin: class FieldPermissionForm(FieldPermissionFormMixin, forms.ModelForm): pass - diff --git a/re2o/management/commands/gen_contrib.py b/re2o/management/commands/gen_contrib.py index 08e57304..06aec202 100644 --- a/re2o/management/commands/gen_contrib.py +++ b/re2o/management/commands/gen_contrib.py @@ -20,18 +20,23 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ -Write in a python file the list of all contributors sorted by number of commits. -This list is extracted from the FedeRez gitlab repository. +Write in a python file the list of all contributors sorted by number of +commits. This list is extracted from the current gitlab repository. """ from django.core.management.base import BaseCommand, CommandError import os + class Command(BaseCommand): - help = 'Update contributors list' + help = 'Update contributors list' def handle(self, *args, **options): - contributeurs = [item.split('\t')[1] for item in os.popen("git shortlog -s -n").read().split("\n") if '\t' in item] + contributeurs = [ + item.split('\t')[1] + for item in os.popen("git shortlog -s -n").read().split("\n") + if '\t' in item + ] self.stdout.write(self.style.SUCCESS("Exportation Sucessfull")) with open("re2o/contributors.py", "w") as contrib_file: contrib_file.write("#!/usr/bin/env python3\n") diff --git a/re2o/mixins.py b/re2o/mixins.py index 307074ff..48102378 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -37,9 +37,15 @@ class RevMixin(object): class FormRevMixin(object): def save(self, *args, **kwargs): if reversion.get_comment() != "" and self.changed_data != []: - reversion.set_comment(reversion.get_comment() + ",%s" % ', '.join(field for field in self.changed_data)) + reversion.set_comment( + reversion.get_comment() + ",%s" + % ', '.join(field for field in self.changed_data) + ) elif self.changed_data: - reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in self.changed_data)) + reversion.set_comment( + "Champs modifié(s) : %s" + % ', '.join(field for field in self.changed_data) + ) return super(FormRevMixin, self).save(*args, **kwargs) @@ -47,12 +53,16 @@ class AclMixin(object): """This mixin is used in nearly every class/models defined in re2o apps. It is used by acl, in models (decorators can_...) and in templates tags :get_instance: Applied on a class, take an id argument, return an instance - :can_create: Applied on a class, take the requested user, return if the user - can do the creation - :can_edit: Applied on an instance, return if the user can edit the instance - :can_delete: Applied on an instance, return if the user can delete the instance - :can_view: Applied on an instance, return if the user can view the instance - :can_view_all: Applied on a class, return if the user can view all instances""" + :can_create: Applied on a class, take the requested user, return if the + user can do the creation + :can_edit: Applied on an instance, return if the user can edit the + instance + :can_delete: Applied on an instance, return if the user can delete the + instance + :can_view: Applied on an instance, return if the user can view the + instance + :can_view_all: Applied on a class, return if the user can view all + instances""" @classmethod def get_classname(cls): @@ -76,8 +86,12 @@ class AclMixin(object): un object :param user_request: instance utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" - return user_request.has_perm(cls.get_modulename() + '.add_' + cls.get_classname()), u"Vous n'avez pas le droit\ - de créer un " + cls.get_classname() + return ( + user_request.has_perm( + cls.get_modulename() + '.add_' + cls.get_classname() + ), + u"Vous n'avez pas le droit de créer un " + cls.get_classname() + ) def can_edit(self, user_request, *args, **kwargs): """Verifie que l'user a les bons droits pour editer @@ -85,7 +99,12 @@ class AclMixin(object): :param self: Instance à editer :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" - return user_request.has_perm(self.get_modulename() + '.change_' + self.get_classname()), u"Vous n'avez pas le droit d'éditer des " + self.get_classname() + return ( + user_request.has_perm( + self.get_modulename() + '.change_' + self.get_classname() + ), + u"Vous n'avez pas le droit d'éditer des " + self.get_classname() + ) def can_delete(self, user_request, *args, **kwargs): """Verifie que l'user a les bons droits pour delete @@ -93,7 +112,12 @@ class AclMixin(object): :param self: Instance à delete :param user_request: Utilisateur qui fait la requête :return: soit True, soit False avec la raison de l'échec""" - return user_request.has_perm(self.get_modulename() + '.delete_' + self.get_classname()), u"Vous n'avez pas le droit d'éditer des " + self.get_classname() + return ( + user_request.has_perm( + self.get_modulename() + '.delete_' + self.get_classname() + ), + u"Vous n'avez pas le droit d'éditer des " + self.get_classname() + ) @classmethod def can_view_all(cls, user_request, *args, **kwargs): @@ -101,7 +125,12 @@ class AclMixin(object): droit particulier view objet correspondant :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - return user_request.has_perm(cls.get_modulename() + '.view_' + cls.get_classname()), u"Vous n'avez pas le droit de voir des " + cls.get_classname() + return ( + user_request.has_perm( + cls.get_modulename() + '.view_' + cls.get_classname() + ), + u"Vous n'avez pas le droit de voir des " + cls.get_classname() + ) def can_view(self, user_request, *args, **kwargs): """Vérifie qu'on peut bien voir cette instance particulière avec @@ -109,4 +138,9 @@ class AclMixin(object): :param self: instance à voir :param user_request: instance user qui fait l'edition :return: True ou False avec la raison de l'échec le cas échéant""" - return user_request.has_perm(self.get_modulename() + '.view_' + self.get_classname()), u"Vous n'avez pas le droit de voir des " + self.get_classname() + return ( + user_request.has_perm( + self.get_modulename() + '.view_' + self.get_classname() + ), + u"Vous n'avez pas le droit de voir des " + self.get_classname() + ) diff --git a/re2o/script_utils.py b/re2o/script_utils.py index e72ea626..27be81ac 100644 --- a/re2o/script_utils.py +++ b/re2o/script_utils.py @@ -19,15 +19,11 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import os, sys, pwd +import os +import sys +import pwd -proj_path="/var/www/re2o" -os.environ.setdefault("DJANGO_SETTINGS_MODULE","re2o.settings") -sys.path.append(proj_path) -os.chdir(proj_path) from django.core.wsgi import get_wsgi_application -application = get_wsgi_application() - from django.core.management.base import CommandError from users.models import User @@ -37,14 +33,22 @@ from reversion import revisions as reversion from django.db import transaction from getpass import getpass +proj_path = "/var/www/re2o" +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "re2o.settings") +sys.path.append(proj_path) +os.chdir(proj_path) + +application = get_wsgi_application() + def get_user(pseudo): """Cherche un utilisateur re2o à partir de son pseudo""" user = User.objects.filter(pseudo=pseudo) - if len(user)==0: + if len(user) == 0: raise CommandError("Utilisateur invalide") - if len(user)>1: - raise CommandError("Plusieurs utilisateurs correspondant à ce pseudo. Ceci NE DEVRAIT PAS arriver") + if len(user) > 1: + raise CommandError("Plusieurs utilisateurs correspondant à ce " + "pseudo. Ceci NE DEVRAIT PAS arriver") return user[0] @@ -53,7 +57,7 @@ def get_system_user(): return pwd.getpwuid(int(os.getenv("SUDO_UID") or os.getuid())).pw_name -def form_cli(Form,user,action,*args,**kwargs): +def form_cli(Form, user, action, *args, **kwargs): """ Remplit un formulaire à partir de la ligne de commande Form : le formulaire (sous forme de classe) à remplir @@ -61,26 +65,30 @@ def form_cli(Form,user,action,*args,**kwargs): action : l'action réalisée par le formulaire (pour les logs) Les arguments suivants sont transmis tels quels au formulaire. """ - data={} - dumb_form = Form(user=user,*args,**kwargs) + data = {} + dumb_form = Form(user=user, *args, **kwargs) for key in dumb_form.fields: - if not dumb_form.fields[key].widget.input_type=='hidden': - if dumb_form.fields[key].widget.input_type=='password': - data[key]=getpass("%s : " % dumb_form.fields[key].label) + if not dumb_form.fields[key].widget.input_type == 'hidden': + if dumb_form.fields[key].widget.input_type == 'password': + data[key] = getpass("%s : " % dumb_form.fields[key].label) else: - data[key]=input("%s : " % dumb_form.fields[key].label) + data[key] = input("%s : " % dumb_form.fields[key].label) - form = Form(data,user=user,*args,**kwargs) + form = Form(data, user=user, *args, **kwargs) if not form.is_valid(): sys.stderr.write("Erreurs : \n") for err in form.errors: - #Oui, oui, on gère du HTML là où d'autres ont eu la lumineuse idée de le mettre - sys.stderr.write("\t%s : %s\n" % (err,strip_tags(form.errors[err]))) + # Oui, oui, on gère du HTML là où d'autres ont eu la + # lumineuse idée de le mettre + sys.stderr.write( + "\t%s : %s\n" % (err, strip_tags(form.errors[err])) + ) raise CommandError("Formulaire invalide") with transaction.atomic(), reversion.create_revision(): - form.save() - reversion.set_user(user) - reversion.set_comment(action) + form.save() + reversion.set_user(user) + reversion.set_comment(action) - sys.stdout.write("%s : effectué. La modification peut prendre quelques minutes pour s'appliquer.\n" % action) + sys.stdout.write("%s : effectué. La modification peut prendre " + "quelques minutes pour s'appliquer.\n" % action) diff --git a/re2o/settings.py b/re2o/settings.py index d3c7c725..66149c53 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -126,7 +126,7 @@ LANGUAGE_CODE = 'en' # Proritary location search for translations # then searches in {app}/locale/ for app in INSTALLED_APPS LOCALE_PATHS = [ - BASE_DIR + '/templates/locale/' # to define translations outside of apps + BASE_DIR + '/templates/locale/' # For translations outside of apps ] TIME_ZONE = 'Europe/Paris' diff --git a/re2o/settings_local.example.py b/re2o/settings_local.example.py index c541840f..1418abfa 100644 --- a/re2o/settings_local.example.py +++ b/re2o/settings_local.example.py @@ -73,23 +73,22 @@ EMAIL_PORT = MY_EMAIL_PORT # Reglages pour la bdd ldap LDAP = { - 'base_user_dn' : 'cn=Utilisateurs,dc=example,dc=org', - 'base_userservice_dn' : 'ou=service-users,dc=example,dc=org', - 'base_usergroup_dn' : 'ou=posix,ou=groups,dc=example,dc=org', - 'base_userservicegroup_dn' : 'ou=services,ou=groups,dc=example,dc=org', - 'user_gid' : 500, + 'base_user_dn': 'cn=Utilisateurs,dc=example,dc=org', + 'base_userservice_dn': 'ou=service-users,dc=example,dc=org', + 'base_usergroup_dn': 'ou=posix,ou=groups,dc=example,dc=org', + 'base_userservicegroup_dn': 'ou=services,ou=groups,dc=example,dc=org', + 'user_gid': 500, } UID_RANGES = { - 'users' : [21001,30000], - 'service-users' : [20000,21000], + 'users': [21001, 30000], + 'service-users': [20000, 21000], } # Chaque groupe a un gid assigné, voici la place libre pour assignation GID_RANGES = { - 'posix' : [501, 600], + 'posix': [501, 600], } OPTIONNAL_APPS = () - diff --git a/re2o/templatetags/__init__.py b/re2o/templatetags/__init__.py index 5be3ef33..cff50933 100644 --- a/re2o/templatetags/__init__.py +++ b/re2o/templatetags/__init__.py @@ -18,4 +18,3 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py index 0f67169f..cb0d5a68 100644 --- a/re2o/templatetags/acl.py +++ b/re2o/templatetags/acl.py @@ -85,32 +85,32 @@ register = template.Library() MODEL_NAME = { # cotisations - 'Facture' : cotisations.models.Facture, - 'Vente' : cotisations.models.Vente, - 'Article' : cotisations.models.Article, - 'Banque' : cotisations.models.Banque, - 'Paiement' : cotisations.models.Paiement, - 'Cotisation' : cotisations.models.Cotisation, + 'Facture': cotisations.models.Facture, + 'Vente': cotisations.models.Vente, + 'Article': cotisations.models.Article, + 'Banque': cotisations.models.Banque, + 'Paiement': cotisations.models.Paiement, + 'Cotisation': cotisations.models.Cotisation, # machines - 'Machine' : machines.models.Machine, - 'MachineType' : machines.models.MachineType, - 'IpType' : machines.models.IpType, - 'Vlan' : machines.models.Vlan, - 'Nas' : machines.models.Nas, - 'SOA' : machines.models.SOA, - 'Extension' : machines.models.Extension, - 'Mx' : machines.models.Mx, - 'Ns' : machines.models.Ns, - 'Txt' : machines.models.Txt, - 'Srv' : machines.models.Srv, - 'Interface' : machines.models.Interface, - 'Domain' : machines.models.Domain, - 'IpList' : machines.models.IpList, - 'Ipv6List' : machines.models.Ipv6List, - 'machines.Service' : machines.models.Service, - 'Service_link' : machines.models.Service_link, - 'OuverturePortList' : machines.models.OuverturePortList, - 'OuverturePort' : machines.models.OuverturePort, + 'Machine': machines.models.Machine, + 'MachineType': machines.models.MachineType, + 'IpType': machines.models.IpType, + 'Vlan': machines.models.Vlan, + 'Nas': machines.models.Nas, + 'SOA': machines.models.SOA, + 'Extension': machines.models.Extension, + 'Mx': machines.models.Mx, + 'Ns': machines.models.Ns, + 'Txt': machines.models.Txt, + 'Srv': machines.models.Srv, + 'Interface': machines.models.Interface, + 'Domain': machines.models.Domain, + 'IpList': machines.models.IpList, + 'Ipv6List': machines.models.Ipv6List, + 'machines.Service': machines.models.Service, + 'Service_link': machines.models.Service_link, + 'OuverturePortList': machines.models.OuverturePortList, + 'OuverturePort': machines.models.OuverturePort, # preferences 'OptionalUser': preferences.models.OptionalUser, 'OptionalMachine': preferences.models.OptionalMachine, @@ -120,25 +120,25 @@ MODEL_NAME = { 'AssoOption': preferences.models.AssoOption, 'MailMessageOption': preferences.models.MailMessageOption, # topologie - 'Stack' : topologie.models.Stack, - 'Switch' : topologie.models.Switch, - 'AccessPoint' : topologie.models.AccessPoint, - 'ModelSwitch' : topologie.models.ModelSwitch, - 'ConstructorSwitch' : topologie.models.ConstructorSwitch, - 'Port' : topologie.models.Port, - 'Room' : topologie.models.Room, - 'Building' : topologie.models.Building, - 'SwitchBay' : topologie.models.SwitchBay, + 'Stack': topologie.models.Stack, + 'Switch': topologie.models.Switch, + 'AccessPoint': topologie.models.AccessPoint, + 'ModelSwitch': topologie.models.ModelSwitch, + 'ConstructorSwitch': topologie.models.ConstructorSwitch, + 'Port': topologie.models.Port, + 'Room': topologie.models.Room, + 'Building': topologie.models.Building, + 'SwitchBay': topologie.models.SwitchBay, # users - 'User' : users.models.User, - 'Adherent' : users.models.Adherent, - 'Club' : users.models.Club, - 'ServiceUser' : users.models.ServiceUser, - 'School' : users.models.School, - 'ListRight' : users.models.ListRight, - 'ListShell' : users.models.ListShell, - 'Ban' : users.models.Ban, - 'Whitelist' : users.models.Whitelist, + 'User': users.models.User, + 'Adherent': users.models.Adherent, + 'Club': users.models.Club, + 'ServiceUser': users.models.ServiceUser, + 'School': users.models.School, + 'ListRight': users.models.ListRight, + 'ListShell': users.models.ListShell, + 'Ban': users.models.Ban, + 'Whitelist': users.models.Whitelist, } @@ -184,17 +184,41 @@ def get_callback(tag_name, obj=None): if tag_name == 'cannot_view_all': return acl_fct(obj.can_view_all, True) if tag_name == 'can_view_app': - return acl_fct(lambda x : (not any(not sys.modules[o].can_view(x) for o in obj), None), False) + return acl_fct( + lambda x: ( + not any(not sys.modules[o].can_view(x) for o in obj), + None + ), + False + ) if tag_name == 'cannot_view_app': - return acl_fct(lambda x : (not any(not sys.modules[o].can_view(x) for o in obj), None), True) + return acl_fct( + lambda x: ( + not any(not sys.modules[o].can_view(x) for o in obj), + None + ), + True + ) if tag_name == 'can_edit_history': - return acl_fct(lambda user:(user.has_perm('admin.change_logentry'),None),False) + return acl_fct( + lambda user: (user.has_perm('admin.change_logentry'), None), + False + ) if tag_name == 'cannot_edit_history': - return acl_fct(lambda user:(user.has_perm('admin.change_logentry'),None),True) + return acl_fct( + lambda user: (user.has_perm('admin.change_logentry'), None), + True + ) if tag_name == 'can_view_any_app': - return acl_fct(lambda x : (any(sys.modules[o].can_view(x) for o in obj), None), False) + return acl_fct( + lambda x: (any(sys.modules[o].can_view(x) for o in obj), None), + False + ) if tag_name == 'cannot_view_any_app': - return acl_fct(lambda x : (any(sys.modules[o].can_view(x) for o in obj), None), True) + return acl_fct( + lambda x: (any(sys.modules[o].can_view(x) for o in obj), None), + True + ) raise template.TemplateSyntaxError( "%r tag is not a valid can_xxx tag" % tag_name @@ -246,11 +270,11 @@ def acl_app_filter(parser, token): tag_name, *app_name = token.split_contents() except ValueError: raise template.TemplateSyntaxError( - "%r tag require 1 argument : an application" + "%r tag require 1 argument: an application" % token.contents.split()[0] ) for name in app_name: - if not name in sys.modules.keys(): + if name not in sys.modules.keys(): raise template.TemplateSyntaxError( "%r is not a registered application for acl." % name @@ -270,6 +294,7 @@ def acl_app_filter(parser, token): return AclNode(callback, oknodes, konodes) + @register.tag('can_change') @register.tag('cannot_change') def acl_change_filter(parser, token): @@ -283,7 +308,7 @@ def acl_change_filter(parser, token): args = tag_content[3:] except ValueError: raise template.TemplateSyntaxError( - "%r tag require at least 2 argument : the model and the field" + "%r tag require at least 2 argument: the model and the field" % token.contents.split()[0] ) @@ -306,6 +331,7 @@ def acl_change_filter(parser, token): return AclNode(callback, oknodes, konodes, *args) + @register.tag('can_create') @register.tag('cannot_create') @register.tag('can_edit_all') @@ -324,7 +350,7 @@ def acl_model_filter(parser, token): args = tag_content[2:] except ValueError: raise template.TemplateSyntaxError( - "%r tag require at least 1 argument : the model" + "%r tag require at least 1 argument: the model" % token.contents.split()[0] ) @@ -364,7 +390,7 @@ def acl_instance_filter(parser, token): args = tag_content[2:] except ValueError: raise template.TemplateSyntaxError( - "%r tag require at least 1 argument : the instance" + "%r tag require at least 1 argument: the instance" % token.contents.split()[0] ) diff --git a/re2o/templatetags/massive_bootstrap_form.py b/re2o/templatetags/massive_bootstrap_form.py index 26a9bcc8..8bfdbee8 100644 --- a/re2o/templatetags/massive_bootstrap_form.py +++ b/re2o/templatetags/massive_bootstrap_form.py @@ -36,13 +36,14 @@ from bootstrap3.forms import render_field register = template.Library() + @register.simple_tag def massive_bootstrap_form(form, mbf_fields, *args, **kwargs): """ Render a form where some specific fields are rendered using Twitter - Typeahead and/or splitree's Bootstrap Tokenfield to improve the performance, the - speed and UX when dealing with very large datasets (select with 50k+ elts - for instance). + Typeahead and/or splitree's Bootstrap Tokenfield to improve the + performance, the speed and UX when dealing with very large datasets + (select with 50k+ elts for instance). When the fields specified should normally be rendered as a select with single selectable option, Twitter Typeahead is used for a better display and the matching query engine. When dealing with multiple selectable @@ -189,8 +190,6 @@ def massive_bootstrap_form(form, mbf_fields, *args, **kwargs): return mbf_form.render() - - class MBFForm(): """ An object to hold all the information and useful methods needed to create and render a massive django form into an actual HTML and JS @@ -198,7 +197,6 @@ class MBFForm(): Every field that is not listed is rendered as a normal bootstrap_field. """ - def __init__(self, form, mbf_fields, *args, **kwargs): # The django form object self.form = form @@ -224,14 +222,13 @@ class MBFForm(): # HTML code to insert inside a template self.html = "" - def render(self): """ HTML code for the fully rendered form with all the necessary form """ for name, field in self.form.fields.items(): - if not name in self.exclude: + if name not in self.exclude: - if name in self.fields and not name in self.hidden_fields: + if name in self.fields and name not in self.hidden_fields: mbf_field = MBFField( name, field, @@ -256,9 +253,6 @@ class MBFForm(): return mark_safe(self.html) - - - class MBFField(): """ An object to hold all the information and useful methods needed to create and render a massive django form field into an actual HTML and JS @@ -270,7 +264,6 @@ class MBFField(): the displayed input. It's used to store the actual data that will be sent to the server """ - def __init__(self, name_, field_, bound_, choices_, engine_, match_func_, update_on_, gen_select_, *args_, **kwargs_): @@ -278,8 +271,8 @@ class MBFField(): if not isinstance(field_.widget, Select): raise ValueError( ('Field named {f_name} is not a Select and' - 'can\'t be rendered with massive_bootstrap_form.' - ).format( + 'can\'t be rendered with massive_bootstrap_form.') + .format( f_name=name_ ) ) @@ -324,7 +317,6 @@ class MBFField(): self.args = args_ self.kwargs = kwargs_ - def default_choices(self): """ JS code of the variable choices_ """ @@ -351,7 +343,6 @@ class MBFField(): ) ) - def default_engine(self): """ Default JS code of the variable engine_ """ return ( @@ -365,7 +356,6 @@ class MBFField(): name=self.name ) - def default_datasets(self): """ Default JS script of the datasets to use with typeahead """ return ( @@ -384,7 +374,6 @@ class MBFField(): match_func=self.match_func ) - def default_match_func(self): """ Default JS code of the matching function to use with typeahed """ return ( @@ -402,14 +391,12 @@ class MBFField(): name=self.name ) - def render(self): """ HTML code for the fully rendered field """ self.gen_displayed_div() self.gen_hidden_div() return mark_safe(self.html) - def gen_displayed_div(self): """ Generate HTML code for the div that contains displayed tags """ if self.gen_select: @@ -434,7 +421,6 @@ class MBFField(): if not self.gen_select: self.html += self.replace_input - def gen_hidden_div(self): """ Generate HTML code for the div that contains hidden tags """ self.gen_full_js() @@ -449,7 +435,6 @@ class MBFField(): attrs={'id': self.div2_id} ) - def hidden_input(self): """ HTML for the hidden input element """ return render_tag( @@ -462,14 +447,12 @@ class MBFField(): } ) - def gen_full_js(self): """ Generate the full script tag containing the JS code """ self.create_js() self.fill_js() self.get_script() - def create_js(self): """ Generate a template for the whole script to use depending on gen_select and multiple """ @@ -549,7 +532,6 @@ class MBFField(): '}} );' ) - def fill_js(self): """ Fill the template with the correct values """ self.js_script = self.js_script.format( @@ -571,11 +553,12 @@ class MBFField(): typ_init_input=self.typeahead_init_input() ) - def get_script(self): """ Insert the JS code inside a script tag """ - self.js_script = render_tag('script', content=mark_safe(self.js_script)) - + self.js_script = render_tag( + 'script', + content=mark_safe(self.js_script) + ) def del_select(self): """ JS code to delete the select if it has been generated and replace @@ -589,7 +572,6 @@ class MBFField(): replace_input=self.replace_input ) - def gen_hidden(self): """ JS code to add a hidden tag to store the value. """ return ( @@ -606,7 +588,6 @@ class MBFField(): html_name=self.bound.html_name ) - def typeahead_init_input(self): """ JS code to init the fields values """ init_key = self.bound.value() or '""' @@ -624,7 +605,6 @@ class MBFField(): hidden_id=self.hidden_id ) - def typeahead_reset_input(self): """ JS code to reset the fields values """ return ( @@ -635,7 +615,6 @@ class MBFField(): hidden_id=self.hidden_id ) - def typeahead_select(self): """ JS code to create the function triggered when an item is selected through typeahead """ @@ -649,7 +628,6 @@ class MBFField(): hidden_id=self.hidden_id ) - def typeahead_change(self): """ JS code of the function triggered when an item is changed (i.e. looses focus and value has changed since the moment it gained focus ) @@ -666,7 +644,6 @@ class MBFField(): hidden_id=self.hidden_id ) - def typeahead_updates(self): """ JS code for binding external fields changes with a reset """ reset_input = self.typeahead_reset_input() @@ -683,7 +660,6 @@ class MBFField(): ) for u_id in self.update_on] return ''.join(updates) - def tokenfield_init_input(self): """ JS code to init the fields values """ init_key = self.bound.value() or '""' @@ -700,7 +676,6 @@ class MBFField(): ) ) - def tokenfield_reset_input(self): """ JS code to reset the fields values """ return ( @@ -709,7 +684,6 @@ class MBFField(): input_id=self.input_id ) - def tokenfield_create(self): """ JS code triggered when a new token is created in tokenfield. """ return ( @@ -739,7 +713,6 @@ class MBFField(): div2_id=self.div2_id ) - def tokenfield_edit(self): """ JS code triggered when a token is edited in tokenfield. """ return ( @@ -765,7 +738,6 @@ class MBFField(): hidden_id=self.hidden_id ) - def tokenfield_remove(self): """ JS code trigggered when a token is removed from tokenfield. """ return ( @@ -791,7 +763,6 @@ class MBFField(): hidden_id=self.hidden_id ) - def tokenfield_updates(self): """ JS code for binding external fields changes with a reset """ reset_input = self.tokenfield_reset_input() diff --git a/re2o/templatetags/self_adhesion.py b/re2o/templatetags/self_adhesion.py index e1577a13..9c749b52 100644 --- a/re2o/templatetags/self_adhesion.py +++ b/re2o/templatetags/self_adhesion.py @@ -24,6 +24,7 @@ from preferences.models import OptionalUser, GeneralOption register = template.Library() + @register.simple_tag def self_adhesion(): options, _created = OptionalUser.objects.get_or_create() diff --git a/re2o/utils.py b/re2o/utils.py index 3bbfffd4..0616bd26 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -118,14 +118,18 @@ def all_has_access(search_time=None): def filter_active_interfaces(interface_set): """Filtre les machines autorisées à sortir sur internet dans une requête""" - return interface_set.filter( - machine__in=Machine.objects.filter( - user__in=all_has_access() - ).filter(active=True) - ).select_related('domain').select_related('machine')\ - .select_related('type').select_related('ipv4')\ - .select_related('domain__extension').select_related('ipv4__ip_type')\ - .distinct() + return (interface_set + .filter( + machine__in=Machine.objects.filter( + user__in=all_has_access() + ).filter(active=True) + ).select_related('domain') + .select_related('machine') + .select_related('type') + .select_related('ipv4') + .select_related('domain__extension') + .select_related('ipv4__ip_type') + .distinct()) def filter_complete_interfaces(interface_set): @@ -160,6 +164,7 @@ def all_active_assigned_interfaces_count(): """ Version light seulement pour compter""" return all_active_interfaces_count().filter(ipv4__isnull=False) + class SortTable: """ 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 @@ -171,7 +176,8 @@ class SortTable: # the url value and the values are a list of model field name to use to # order the request. They are applied in the order they are given. # A 'default' might be provided to specify what to do if the requested col - # doesn't match any keys. + # doesn't match any keys. + USERS_INDEX = { 'user_name': ['name'], 'user_surname': ['surname'], @@ -255,7 +261,7 @@ class SortTable: } TOPOLOGIE_INDEX_MODEL_SWITCH = { 'model-switch_name': ['reference'], - 'model-switch_contructor' : ['constructor__name'], + 'model-switch_contructor': ['constructor__name'], 'default': ['reference'], } TOPOLOGIE_INDEX_SWITCH_BAY = { @@ -290,6 +296,7 @@ class SortTable: else: return request + def re2o_paginator(request, query_set, pagination_number): """Paginator script for list display in re2o. :request: @@ -307,6 +314,7 @@ def re2o_paginator(request, query_set, pagination_number): results = paginator.page(paginator.num_pages) return results + def remove_user_room(room): """ Déménage de force l'ancien locataire de la chambre """ try: diff --git a/re2o/views.py b/re2o/views.py index 4d7e47d0..f1729fb4 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -44,12 +44,17 @@ from itertools import chain from preferences.models import Service from preferences.models import OptionalUser, GeneralOption, AssoOption -import users, preferences, cotisations, topologie, machines +import users +import preferences +import cotisations +import topologie +import machines from .utils import re2o_paginator from .settings import BASE_DIR, INSTALLED_APPS, MIDDLEWARE_CLASSES from .contributors import CONTRIBUTORS + def form(ctx, template, request): """Form générique, raccourci importé par les fonctions views du site""" context = ctx @@ -64,56 +69,58 @@ def index(request): services[indice % 3].append(serv) return form({'services_urls': services}, 're2o/index.html', request) + #: Binding the corresponding char sequence of history url to re2o models. HISTORY_BIND = { - 'users' : { - 'user' : users.models.User, - 'ban' : users.models.Ban, - 'whitelist' : users.models.Whitelist, - 'school' : users.models.School, - 'listright' : users.models.ListRight, - 'serviceuser' : users.models.ServiceUser, - 'listshell' : users.models.ListShell, + 'users': { + 'user': users.models.User, + 'ban': users.models.Ban, + 'whitelist': users.models.Whitelist, + 'school': users.models.School, + 'listright': users.models.ListRight, + 'serviceuser': users.models.ServiceUser, + 'listshell': users.models.ListShell, }, - 'preferences' : { - 'service' : preferences.models.Service, + 'preferences': { + 'service': preferences.models.Service, }, - 'cotisations' : { - 'facture' : cotisations.models.Facture, - 'article' : cotisations.models.Article, - 'paiement' : cotisations.models.Paiement, - 'banque' : cotisations.models.Banque, + 'cotisations': { + 'facture': cotisations.models.Facture, + 'article': cotisations.models.Article, + 'paiement': cotisations.models.Paiement, + 'banque': cotisations.models.Banque, }, - 'topologie' : { - 'switch' : topologie.models.Switch, - 'port' : topologie.models.Port, - 'room' : topologie.models.Room, - 'stack' : topologie.models.Stack, - 'modelswitch' : topologie.models.ModelSwitch, - 'constructorswitch' : topologie.models.ConstructorSwitch, - 'accesspoint' : topologie.models.AccessPoint, - 'switchbay' : topologie.models.SwitchBay, - 'building' : topologie.models.Building, + 'topologie': { + 'switch': topologie.models.Switch, + 'port': topologie.models.Port, + 'room': topologie.models.Room, + 'stack': topologie.models.Stack, + 'modelswitch': topologie.models.ModelSwitch, + 'constructorswitch': topologie.models.ConstructorSwitch, + 'accesspoint': topologie.models.AccessPoint, + 'switchbay': topologie.models.SwitchBay, + 'building': topologie.models.Building, }, - 'machines' : { - 'machine' : machines.models.Machine, - 'interface' : machines.models.Interface, - 'domain' : machines.models.Domain, - 'machinetype' : machines.models.MachineType, - 'iptype' : machines.models.IpType, - 'extension' : machines.models.Extension, - 'soa' : machines.models.SOA, - 'mx' : machines.models.Mx, - 'txt' : machines.models.Txt, - 'srv' : machines.models.Srv, - 'ns' : machines.models.Ns, - 'service' : machines.models.Service, - 'vlan' : machines.models.Vlan, - 'nas' : machines.models.Nas, - 'ipv6list' : machines.models.Ipv6List, + 'machines': { + 'machine': machines.models.Machine, + 'interface': machines.models.Interface, + 'domain': machines.models.Domain, + 'machinetype': machines.models.MachineType, + 'iptype': machines.models.IpType, + 'extension': machines.models.Extension, + 'soa': machines.models.SOA, + 'mx': machines.models.Mx, + 'txt': machines.models.Txt, + 'srv': machines.models.Srv, + 'ns': machines.models.Ns, + 'service': machines.models.Service, + 'vlan': machines.models.Vlan, + 'nas': machines.models.Nas, + 'ipv6list': machines.models.Ipv6List, }, } + @login_required def history(request, application, object_name, object_id): """Render history for a model. @@ -144,21 +151,23 @@ def history(request, application, object_name, object_id): instance = model.get_instance(**kwargs) except model.DoesNotExist: messages.error(request, u"Entrée inexistante") - return redirect(reverse('users:profil', - kwargs={'userid':str(request.user.id)} + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} )) can, msg = instance.can_view(request.user) if not can: messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu") return redirect(reverse( 'users:profil', - kwargs={'userid':str(request.user.id)} + kwargs={'userid': str(request.user.id)} )) pagination_number = GeneralOption.get_cached_value('pagination_number') reversions = Version.objects.get_for_object(instance) if hasattr(instance, 'linked_objects'): for related_object in chain(instance.linked_objects()): - reversions = reversions | Version.objects.get_for_object(related_object) + reversions = (reversions | + Version.objects.get_for_object(related_object)) reversions = re2o_paginator(request, reversions, pagination_number) return render( request, @@ -191,8 +200,8 @@ def about_page(request): request, "re2o/about.html", { - 'description': option.description , - 'AssoName' : option.name , + 'description': option.description, + 'AssoName': option.name, 'git_info_contributors': git_info_contributors, 'git_info_remote': git_info_remote, 'git_info_branch': git_info_branch, @@ -201,4 +210,3 @@ def about_page(request): 'dependencies': dependencies } ) - From 8afc8e4304d4d6e9ee1b839289b9384ee37e7dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 00:20:44 +0000 Subject: [PATCH 07/22] Pep8 compliance on topologie --- search/acl.py | 1 + search/views.py | 2 +- topologie/acl.py | 1 + topologie/forms.py | 71 +++++--- topologie/models.py | 9 +- topologie/urls.py | 28 ++- topologie/views.py | 428 +++++++++++++++++++++++++++++--------------- 7 files changed, 357 insertions(+), 183 deletions(-) diff --git a/search/acl.py b/search/acl.py index 5c80e473..f2b289bf 100644 --- a/search/acl.py +++ b/search/acl.py @@ -26,6 +26,7 @@ Here are defined some functions to check acl on the application. """ + def can_view(user): """Check if an user can view the application. diff --git a/search/views.py b/search/views.py index 732ec510..871515fa 100644 --- a/search/views.py +++ b/search/views.py @@ -144,7 +144,7 @@ def search_single_word(word, filters, user, if not User.can_view_all(user)[0]: filter_users &= Q(id=user.id) filter_clubs = filter_users - filter_users |= Q(name__icontains=word) + filter_users |= Q(name__icontains=word) filters['users'] |= filter_users filters['clubs'] |= filter_clubs diff --git a/topologie/acl.py b/topologie/acl.py index aa6adbde..ebef17c9 100644 --- a/topologie/acl.py +++ b/topologie/acl.py @@ -26,6 +26,7 @@ Here are defined some functions to check acl on the application. """ + def can_view(user): """Check if an user can view the application. diff --git a/topologie/forms.py b/topologie/forms.py index bb0c2888..013321b6 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -41,7 +41,7 @@ from machines.forms import ( from django import forms from django.forms import ModelForm, Form from django.db.models import Prefetch -from .models import ( +from .models import ( Port, Switch, Room, @@ -54,6 +54,7 @@ from .models import ( ) from re2o.mixins import FormRevMixin + class PortForm(FormRevMixin, ModelForm): """Formulaire pour la création d'un port d'un switch Relié directement au modèle port""" @@ -82,32 +83,48 @@ class EditPortForm(FormRevMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(EditPortForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['machine_interface'].queryset = Interface.objects.all()\ - .select_related('domain__extension') - self.fields['related'].queryset = Port.objects.all()\ + self.fields['machine_interface'].queryset = ( + Interface.objects.all().select_related('domain__extension') + ) + self.fields['related'].queryset = ( + Port.objects.all() .prefetch_related(Prefetch( - 'switch__interface_set', - queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension') + 'switch__interface_set', + queryset=(Interface.objects + .select_related('ipv4__ip_type__extension') + .select_related('domain__extension')) )) + ) class AddPortForm(FormRevMixin, ModelForm): """Permet d'ajouter un port de switch. Voir EditPortForm pour plus d'informations""" class Meta(PortForm.Meta): - fields = ['port', 'room', 'machine_interface', 'related', - 'radius', 'vlan_force', 'details'] + fields = [ + 'port', + 'room', + 'machine_interface', + 'related', + 'radius', + 'vlan_force', + 'details' + ] def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(AddPortForm, self).__init__(*args, prefix=prefix, **kwargs) - self.fields['machine_interface'].queryset = Interface.objects.all()\ - .select_related('domain__extension') - self.fields['related'].queryset = Port.objects.all()\ - .prefetch_related(Prefetch( - 'switch__interface_set', - queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension') + self.fields['machine_interface'].queryset = ( + Interface.objects.all().select_related('domain__extension') + ) + self.fields['related'].queryset = ( + Port.objects.all().prefetch_related(Prefetch( + 'switch__interface_set', + queryset=(Interface.objects + .select_related('ipv4__ip_type__extension') + .select_related('domain__extension')) )) + ) class StackForm(FormRevMixin, ModelForm): @@ -170,15 +187,22 @@ class CreatePortsForm(forms.Form): class EditModelSwitchForm(FormRevMixin, ModelForm): """Permet d'éediter un modèle de switch : nom et constructeur""" - members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False) - + members = forms.ModelMultipleChoiceField( + Switch.objects.all(), + required=False + ) + class Meta: model = ModelSwitch fields = '__all__' def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditModelSwitchForm, self).__init__(*args, prefix=prefix, **kwargs) + super(EditModelSwitchForm, self).__init__( + *args, + prefix=prefix, + **kwargs + ) instance = kwargs.get('instance', None) if instance: self.initial['members'] = Switch.objects.filter(model=instance) @@ -197,13 +221,20 @@ class EditConstructorSwitchForm(FormRevMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(EditConstructorSwitchForm, self).__init__(*args, prefix=prefix, **kwargs) + super(EditConstructorSwitchForm, self).__init__( + *args, + prefix=prefix, + **kwargs + ) class EditSwitchBayForm(FormRevMixin, ModelForm): """Permet d'éditer une baie de brassage""" - members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False) - + members = forms.ModelMultipleChoiceField( + Switch.objects.all(), + required=False + ) + class Meta: model = SwitchBay fields = '__all__' diff --git a/topologie/models.py b/topologie/models.py index 2a42957b..2cb37c63 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -50,6 +50,7 @@ from reversion import revisions as reversion from machines.models import Machine, Interface, regen from re2o.mixins import AclMixin, RevMixin + class Stack(AclMixin, RevMixin, models.Model): """Un objet stack. Regrouppe des switchs en foreign key ,contient une id de stack, un switch id min et max dans @@ -85,12 +86,12 @@ class Stack(AclMixin, RevMixin, models.Model): class AccessPoint(AclMixin, Machine): """Define a wireless AP. Inherit from machines.interfaces - + Definition pour une borne wifi , hérite de machines.interfaces """ PRETTY_NAME = "Borne WiFi" - location = models.CharField( + location = models.CharField( max_length=255, help_text="Détails sur la localisation de l'AP", blank=True, @@ -120,7 +121,6 @@ class Switch(AclMixin, Machine): id_max de la stack parente""" PRETTY_NAME = "Switch / Commutateur" - number = models.PositiveIntegerField() stack = models.ForeignKey( 'topologie.Stack', @@ -165,7 +165,8 @@ class Switch(AclMixin, Machine): ne peut être nul"}) def create_ports(self, begin, end): - """ Crée les ports de begin à end si les valeurs données sont cohérentes. """ + """ Crée les ports de begin à end si les valeurs données + sont cohérentes. """ s_begin = s_end = 0 nb_ports = self.ports.count() diff --git a/topologie/urls.py b/topologie/urls.py index 9b0a7065..af3327b7 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -51,12 +51,10 @@ urlpatterns = [ url(r'^switch/(?P[0-9]+)$', views.index_port, name='index-port'), - url( - r'^history/(?P\w+)/(?P[0-9]+)$', + url(r'^history/(?P\w+)/(?P[0-9]+)$', re2o.views.history, name='history', - kwargs={'application':'topologie'}, - ), + kwargs={'application': 'topologie'}), url(r'^edit_port/(?P[0-9]+)$', views.edit_port, name='edit-port'), url(r'^new_port/(?P[0-9]+)$', views.new_port, name='new-port'), url(r'^del_port/(?P[0-9]+)$', views.del_port, name='del-port'), @@ -64,7 +62,9 @@ urlpatterns = [ views.edit_switch, name='edit-switch'), url(r'^new_stack/$', views.new_stack, name='new-stack'), - url(r'^index_physical_grouping/$', views.index_physical_grouping, name='index-physical-grouping'), + url(r'^index_physical_grouping/$', + views.index_physical_grouping, + name='index-physical-grouping'), url(r'^edit_stack/(?P[0-9]+)$', views.edit_stack, name='edit-stack'), @@ -73,16 +73,13 @@ urlpatterns = [ name='del-stack'), url(r'^index_model_switch/$', views.index_model_switch, - name='index-model-switch' - ), + name='index-model-switch'), url(r'^index_model_switch/$', views.index_model_switch, - name='index-model-switch' - ), + name='index-model-switch'), url(r'^new_model_switch/$', views.new_model_switch, - name='new-model-switch' - ), + name='new-model-switch'), url(r'^edit_model_switch/(?P[0-9]+)$', views.edit_model_switch, name='edit-model-switch'), @@ -91,8 +88,7 @@ urlpatterns = [ name='del-model-switch'), url(r'^new_constructor_switch/$', views.new_constructor_switch, - name='new-constructor-switch' - ), + name='new-constructor-switch'), url(r'^edit_constructor_switch/(?P[0-9]+)$', views.edit_constructor_switch, name='edit-constructor-switch'), @@ -101,8 +97,7 @@ urlpatterns = [ name='del-constructor-switch'), url(r'^new_switch_bay/$', views.new_switch_bay, - name='new-switch-bay' - ), + name='new-switch-bay'), url(r'^edit_switch_bay/(?P[0-9]+)$', views.edit_switch_bay, name='edit-switch-bay'), @@ -111,8 +106,7 @@ urlpatterns = [ name='del-switch-bay'), url(r'^new_building/$', views.new_building, - name='new-building' - ), + name='new-building'), url(r'^edit_building/(?P[0-9]+)$', views.edit_building, name='edit-building'), diff --git a/topologie/views.py b/topologie/views.py index 6c18721f..12bea920 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -93,12 +93,14 @@ from preferences.models import AssoOption, GeneralOption @can_view_all(Switch) def index(request): """ Vue d'affichage de tous les swicthes""" - switch_list = Switch.objects\ - .prefetch_related(Prefetch( - 'interface_set', - queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension') - ))\ - .select_related('stack') + switch_list = (Switch.objects + .prefetch_related(Prefetch( + 'interface_set', + queryset=(Interface.objects + .select_related('ipv4__ip_type__extension') + .select_related('domain__extension')) + )) + .select_related('stack')) switch_list = SortTable.sort( switch_list, request.GET.get('col'), @@ -107,9 +109,11 @@ def index(request): ) pagination_number = GeneralOption.get_cached_value('pagination_number') switch_list = re2o_paginator(request, switch_list, pagination_number) - return render(request, 'topologie/index.html', { - 'switch_list': switch_list - }) + return render( + request, + 'topologie/index.html', + {'switch_list': switch_list} + ) @login_required @@ -117,27 +121,33 @@ def index(request): @can_view(Switch) def index_port(request, switch, switchid): """ Affichage de l'ensemble des ports reliés à un switch particulier""" - port_list = Port.objects.filter(switch=switch)\ - .select_related('room')\ - .select_related('machine_interface__domain__extension')\ - .select_related('machine_interface__machine__user')\ - .select_related('related__switch')\ - .prefetch_related(Prefetch( - 'related__switch__interface_set', - queryset=Interface.objects.select_related('domain__extension') - ))\ - .select_related('switch') + port_list = (Port.objects + .filter(switch=switch) + .select_related('room') + .select_related('machine_interface__domain__extension') + .select_related('machine_interface__machine__user') + .select_related('related__switch') + .prefetch_related(Prefetch( + 'related__switch__interface_set', + queryset=(Interface.objects + .select_related('domain__extension')) + )) + .select_related('switch')) port_list = SortTable.sort( port_list, request.GET.get('col'), request.GET.get('order'), SortTable.TOPOLOGIE_INDEX_PORT ) - return render(request, 'topologie/index_p.html', { - 'port_list': port_list, - 'id_switch': switchid, - 'nom_switch': switch - }) + return render( + request, + 'topologie/index_p.html', + { + 'port_list': port_list, + 'id_switch': switchid, + 'nom_switch': switch + } + ) @login_required @@ -153,20 +163,24 @@ def index_room(request): ) pagination_number = GeneralOption.get_cached_value('pagination_number') room_list = re2o_paginator(request, room_list, pagination_number) - return render(request, 'topologie/index_room.html', { - 'room_list': room_list - }) + return render( + request, + 'topologie/index_room.html', + {'room_list': room_list} + ) @login_required @can_view_all(AccessPoint) def index_ap(request): """ Affichage de l'ensemble des bornes""" - ap_list = AccessPoint.objects\ - .prefetch_related(Prefetch( - 'interface_set', - queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension') - )) + ap_list = (AccessPoint.objects + .prefetch_related(Prefetch( + 'interface_set', + queryset=(Interface.objects + .select_related('ipv4__ip_type__extension') + .select_related('domain__extension')) + ))) ap_list = SortTable.sort( ap_list, request.GET.get('col'), @@ -175,9 +189,11 @@ def index_ap(request): ) pagination_number = GeneralOption.get_cached_value('pagination_number') ap_list = re2o_paginator(request, ap_list, pagination_number) - return render(request, 'topologie/index_ap.html', { - 'ap_list': ap_list - }) + return render( + request, + 'topologie/index_ap.html', + {'ap_list': ap_list} + ) @login_required @@ -186,8 +202,10 @@ def index_ap(request): @can_view_all(SwitchBay) def index_physical_grouping(request): """Affichage de la liste des stacks (affiche l'ensemble des switches)""" - stack_list = Stack.objects\ - .prefetch_related('switch_set__interface_set__domain__extension') + stack_list = (Stack.objects + .prefetch_related( + 'switch_set__interface_set__domain__extension' + )) building_list = Building.objects.all() switch_bay_list = SwitchBay.objects.select_related('building') stack_list = SortTable.sort( @@ -208,11 +226,15 @@ def index_physical_grouping(request): request.GET.get('order'), SortTable.TOPOLOGIE_INDEX_SWITCH_BAY ) - return render(request, 'topologie/index_physical_grouping.html', { - 'stack_list': stack_list, - 'switch_bay_list': switch_bay_list, - 'building_list' : building_list, - }) + return render( + request, + 'topologie/index_physical_grouping.html', + { + 'stack_list': stack_list, + 'switch_bay_list': switch_bay_list, + 'building_list': building_list, + } + ) @login_required @@ -234,10 +256,14 @@ def index_model_switch(request): request.GET.get('order'), SortTable.TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH ) - return render(request, 'topologie/index_model_switch.html', { - 'model_switch_list': model_switch_list, - 'constructor_switch_list': constructor_switch_list, - }) + return render( + request, + 'topologie/index_model_switch.html', + { + 'model_switch_list': model_switch_list, + 'constructor_switch_list': constructor_switch_list, + } + ) @login_required @@ -260,9 +286,12 @@ def new_port(request, switchid): messages.error(request, "Ce port existe déjà") return redirect(reverse( 'topologie:index-port', - kwargs={'switchid':switchid} - )) - return form({'id_switch': switchid,'topoform': port, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request) + kwargs={'switchid': switchid} + )) + return form( + {'id_switch': switchid, 'topoform': port, 'action_name': 'Ajouter'}, + 'topologie/topo.html', + request) @login_required @@ -279,8 +308,16 @@ def edit_port(request, port_object, portid): return redirect(reverse( 'topologie:index-port', kwargs={'switchid': str(port_object.switch.id)} - )) - return form({'id_switch': str(port_object.switch.id), 'topoform': port, 'action_name' : 'Editer'}, 'topologie/topo.html', request) + )) + return form( + { + 'id_switch': str(port_object.switch.id), + 'topoform': port, + 'action_name': 'Editer' + }, + 'topologie/topo.html', + request + ) @login_required @@ -292,12 +329,15 @@ def del_port(request, port, portid): port.delete() messages.success(request, "Le port a été détruit") except ProtectedError: - messages.error(request, "Le port %s est affecté à un autre objet,\ - impossible de le supprimer" % port) + messages.error( + request, + ("Le port %s est affecté à un autre objet, impossible " + "de le supprimer" % port) + ) return redirect(reverse( 'topologie:index-port', - kwargs={'switchid':str(port.switch.id)} - )) + kwargs={'switchid': str(port.switch.id)} + )) return form({'objet': port}, 'topologie/delete.html', request) @@ -309,7 +349,11 @@ def new_stack(request): if stack.is_valid(): stack.save() messages.success(request, "Stack crée") - return form({'topoform': stack, 'action_name' : 'Créer'}, 'topologie/topo.html', request) + return form( + {'topoform': stack, 'action_name': 'Créer'}, + 'topologie/topo.html', + request + ) @login_required @@ -321,7 +365,11 @@ def edit_stack(request, stack, stackid): if stack.changed_data: stack.save() return redirect(reverse('topologie:index-physical-grouping')) - return form({'topoform': stack, 'action_name' : 'Editer'}, 'topologie/topo.html', request) + return form( + {'topoform': stack, 'action_name': 'Editer'}, + 'topologie/topo.html', + request + ) @login_required @@ -333,8 +381,11 @@ def del_stack(request, stack, stackid): stack.delete() messages.success(request, "La stack a eté détruite") except ProtectedError: - messages.error(request, "La stack %s est affectée à un autre\ - objet, impossible de la supprimer" % stack) + messages.error( + request, + ("La stack %s est affectée à un autre objet, impossible " + "de la supprimer" % stack) + ) return redirect(reverse('topologie:index-physical-grouping')) return form({'objet': stack}, 'topologie/delete.html', request) @@ -372,8 +423,11 @@ def new_switch(request): if switch.is_valid() and interface.is_valid(): user = AssoOption.get_cached_value('utilisateur_asso') if not user: - messages.error(request, "L'user association n'existe pas encore,\ - veuillez le créer ou le linker dans preferences") + messages.error( + request, + ("L'user association n'existe pas encore, veuillez le " + "créer ou le linker dans preferences") + ) return redirect(reverse('topologie:index')) new_switch = switch.save(commit=False) new_switch.user = user @@ -389,13 +443,17 @@ def new_switch(request): messages.success(request, "Le switch a été créé") return redirect(reverse('topologie:index')) i_mbf_param = generate_ipv4_mbf_param(interface, False) - return form({ - 'topoform': interface, - 'machineform': switch, - 'domainform': domain, - 'i_mbf_param': i_mbf_param, - 'device' : 'switch', - }, 'topologie/topo_more.html', request) + return form( + { + 'topoform': interface, + 'machineform': switch, + 'domainform': domain, + 'i_mbf_param': i_mbf_param, + 'device': 'switch', + }, + 'topologie/topo_more.html', + request + ) @login_required @@ -430,9 +488,13 @@ def create_ports(request, switchid): messages.error(request, ''.join(e)) return redirect(reverse( 'topologie:index-port', - kwargs={'switchid':switchid} + kwargs={'switchid': switchid} )) - return form({'id_switch': switchid, 'topoform': port_form}, 'topologie/switch.html', request) + return form( + {'id_switch': switchid, 'topoform': port_form}, + 'topologie/switch.html', + request + ) @login_required @@ -467,15 +529,19 @@ def edit_switch(request, switch, switchid): new_domain.save() messages.success(request, "Le switch a bien été modifié") return redirect(reverse('topologie:index')) - i_mbf_param = generate_ipv4_mbf_param(interface_form, False ) - return form({ - 'id_switch': switchid, - 'topoform': interface_form, - 'machineform': switch_form, - 'domainform': domain_form, - 'i_mbf_param': i_mbf_param, - 'device' : 'switch', - }, 'topologie/topo_more.html', request) + i_mbf_param = generate_ipv4_mbf_param(interface_form, False) + return form( + { + 'id_switch': switchid, + 'topoform': interface_form, + 'machineform': switch_form, + 'domainform': domain_form, + 'i_mbf_param': i_mbf_param, + 'device': 'switch', + }, + 'topologie/topo_more.html', + request + ) @login_required @@ -498,8 +564,11 @@ def new_ap(request): if ap.is_valid() and interface.is_valid(): user = AssoOption.get_cached_value('utilisateur_asso') if not user: - messages.error(request, "L'user association n'existe pas encore,\ - veuillez le créer ou le linker dans preferences") + messages.error( + request, + ("L'user association n'existe pas encore, veuillez le " + "créer ou le linker dans preferences") + ) return redirect(reverse('topologie:index')) new_ap = ap.save(commit=False) new_ap.user = user @@ -515,13 +584,17 @@ def new_ap(request): messages.success(request, "La borne a été créé") return redirect(reverse('topologie:index-ap')) i_mbf_param = generate_ipv4_mbf_param(interface, False) - return form({ - 'topoform': interface, - 'machineform': ap, - 'domainform': domain, - 'i_mbf_param': i_mbf_param, - 'device' : 'wifi ap', - }, 'topologie/topo_more.html', request) + return form( + { + 'topoform': interface, + 'machineform': ap, + 'domainform': domain, + 'i_mbf_param': i_mbf_param, + 'device': 'wifi ap', + }, + 'topologie/topo_more.html', + request + ) @login_required @@ -546,8 +619,11 @@ def edit_ap(request, ap, accesspointid): if ap_form.is_valid() and interface_form.is_valid(): user = AssoOption.get_cached_value('utilisateur_asso') if not user: - messages.error(request, "L'user association n'existe pas encore,\ - veuillez le créer ou le linker dans preferences") + messages.error( + request, + ("L'user association n'existe pas encore, veuillez le " + "créer ou le linker dans preferences") + ) return redirect(reverse('topologie:index-ap')) new_ap = ap_form.save(commit=False) new_interface = interface_form.save(commit=False) @@ -560,15 +636,19 @@ def edit_ap(request, ap, accesspointid): new_domain.save() messages.success(request, "La borne a été modifiée") return redirect(reverse('topologie:index-ap')) - i_mbf_param = generate_ipv4_mbf_param(interface_form, False ) - return form({ - 'topoform': interface_form, - 'machineform': ap_form, - 'domainform': domain_form, - 'i_mbf_param': i_mbf_param, - 'device' : 'wifi ap', - }, 'topologie/topo_more.html', request) - + i_mbf_param = generate_ipv4_mbf_param(interface_form, False) + return form( + { + 'topoform': interface_form, + 'machineform': ap_form, + 'domainform': domain_form, + 'i_mbf_param': i_mbf_param, + 'device': 'wifi ap', + }, + 'topologie/topo_more.html', + request + ) + @login_required @can_create(Room) @@ -579,7 +659,11 @@ def new_room(request): room.save() messages.success(request, "La chambre a été créé") return redirect(reverse('topologie:index-room')) - return form({'topoform': room, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request) + return form( + {'topoform': room, 'action_name': 'Ajouter'}, + 'topologie/topo.html', + request + ) @login_required @@ -592,7 +676,11 @@ def edit_room(request, room, roomid): room.save() messages.success(request, "La chambre a bien été modifiée") return redirect(reverse('topologie:index-room')) - return form({'topoform': room, 'action_name' : 'Editer'}, 'topologie/topo.html', request) + return form( + {'topoform': room, 'action_name': 'Editer'}, + 'topologie/topo.html', + request + ) @login_required @@ -604,13 +692,17 @@ def del_room(request, room, roomid): room.delete() messages.success(request, "La chambre/prise a été détruite") except ProtectedError: - messages.error(request, "La chambre %s est affectée à un autre objet,\ - impossible de la supprimer (switch ou user)" % room) + messages.error( + request, + ("La chambre %s est affectée à un autre objet, impossible " + "de la supprimer (switch ou user)" % room) + ) return redirect(reverse('topologie:index-room')) - return form({ - 'objet': room, - 'objet_name': 'Chambre' - }, 'topologie/delete.html', request) + return form( + {'objet': room, 'objet_name': 'Chambre'}, + 'topologie/delete.html', + request + ) @login_required @@ -622,7 +714,11 @@ def new_model_switch(request): model_switch.save() messages.success(request, "Le modèle a été créé") return redirect(reverse('topologie:index-model-switch')) - return form({'topoform': model_switch, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request) + return form( + {'topoform': model_switch, 'action_name': 'Ajouter'}, + 'topologie/topo.html', + request + ) @login_required @@ -630,13 +726,20 @@ def new_model_switch(request): def edit_model_switch(request, model_switch, modelswitchid): """ Edition d'un modèle de switch""" - model_switch = EditModelSwitchForm(request.POST or None, instance=model_switch) + model_switch = EditModelSwitchForm( + request.POST or None, + instance=model_switch + ) if model_switch.is_valid(): if model_switch.changed_data: model_switch.save() messages.success(request, "Le modèle a bien été modifié") return redirect(reverse('topologie:index-model-switch')) - return form({'topoform': model_switch, 'action_name' : 'Editer'}, 'topologie/topo.html', request) + return form( + {'topoform': model_switch, 'action_name': 'Editer'}, + 'topologie/topo.html', + request + ) @login_required @@ -648,13 +751,17 @@ def del_model_switch(request, model_switch, modelswitchid): model_switch.delete() messages.success(request, "Le modèle a été détruit") except ProtectedError: - messages.error(request, "Le modèle %s est affectée à un autre objet,\ - impossible de la supprimer (switch ou user)" % model_switch) + messages.error( + request, + ("Le modèle %s est affectée à un autre objet, impossible " + "de la supprimer (switch ou user)" % model_switch) + ) return redirect(reverse('topologie:index-model-switch')) - return form({ - 'objet': model_switch, - 'objet_name': 'Modèle de switch' - }, 'topologie/delete.html', request) + return form( + {'objet': model_switch, 'objet_name': 'Modèle de switch'}, + 'topologie/delete.html', + request + ) @login_required @@ -666,7 +773,11 @@ def new_switch_bay(request): switch_bay.save() messages.success(request, "La baie a été créé") return redirect(reverse('topologie:index-physical-grouping')) - return form({'topoform': switch_bay, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request) + return form( + {'topoform': switch_bay, 'action_name': 'Ajouter'}, + 'topologie/topo.html', + request + ) @login_required @@ -679,7 +790,11 @@ def edit_switch_bay(request, switch_bay, switchbayid): switch_bay.save() messages.success(request, "Le switch a bien été modifié") return redirect(reverse('topologie:index-physical-grouping')) - return form({'topoform': switch_bay, 'action_name' : 'Editer'}, 'topologie/topo.html', request) + return form( + {'topoform': switch_bay, 'action_name': 'Editer'}, + 'topologie/topo.html', + request + ) @login_required @@ -691,13 +806,17 @@ def del_switch_bay(request, switch_bay, switchbayid): switch_bay.delete() messages.success(request, "La baie a été détruite") except ProtectedError: - messages.error(request, "La baie %s est affecté à un autre objet,\ - impossible de la supprimer (switch ou user)" % switch_bay) + messages.error( + request, + ("La baie %s est affecté à un autre objet, impossible " + "de la supprimer (switch ou user)" % switch_bay) + ) return redirect(reverse('topologie:index-physical-grouping')) - return form({ - 'objet': switch_bay, - 'objet_name': 'Baie de switch' - }, 'topologie/delete.html', request) + return form( + {'objet': switch_bay, 'objet_name': 'Baie de switch'}, + 'topologie/delete.html', + request + ) @login_required @@ -709,7 +828,11 @@ def new_building(request): building.save() messages.success(request, "Le batiment a été créé") return redirect(reverse('topologie:index-physical-grouping')) - return form({'topoform': building, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request) + return form( + {'topoform': building, 'action_name': 'Ajouter'}, + 'topologie/topo.html', + request + ) @login_required @@ -722,7 +845,11 @@ def edit_building(request, building, buildingid): building.save() messages.success(request, "Le batiment a bien été modifié") return redirect(reverse('topologie:index-physical-grouping')) - return form({'topoform': building, 'action_name' : 'Editer'}, 'topologie/topo.html', request) + return form( + {'topoform': building, 'action_name': 'Editer'}, + 'topologie/topo.html', + request + ) @login_required @@ -734,13 +861,17 @@ def del_building(request, building, buildingid): building.delete() messages.success(request, "La batiment a été détruit") except ProtectedError: - messages.error(request, "Le batiment %s est affecté à un autre objet,\ - impossible de la supprimer (switch ou user)" % building) + messages.error( + request, + ("Le batiment %s est affecté à un autre objet, impossible " + "de la supprimer (switch ou user)" % building) + ) return redirect(reverse('topologie:index-physical-grouping')) - return form({ - 'objet': building, - 'objet_name': 'Bâtiment' - }, 'topologie/delete.html', request) + return form( + {'objet': building, 'objet_name': 'Bâtiment'}, + 'topologie/delete.html', + request + ) @login_required @@ -752,7 +883,11 @@ def new_constructor_switch(request): constructor_switch.save() messages.success(request, "Le constructeur a été créé") return redirect(reverse('topologie:index-model-switch')) - return form({'topoform': constructor_switch, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request) + return form( + {'topoform': constructor_switch, 'action_name': 'Ajouter'}, + 'topologie/topo.html', + request + ) @login_required @@ -760,13 +895,20 @@ def new_constructor_switch(request): def edit_constructor_switch(request, constructor_switch, constructorswitchid): """ Edition d'un constructeur de switch""" - constructor_switch = EditConstructorSwitchForm(request.POST or None, instance=constructor_switch) + constructor_switch = EditConstructorSwitchForm( + request.POST or None, + instance=constructor_switch + ) if constructor_switch.is_valid(): if constructor_switch.changed_data: constructor_switch.save() messages.success(request, "Le modèle a bien été modifié") return redirect(reverse('topologie:index-model-switch')) - return form({'topoform': constructor_switch, 'action_name' : 'Editer'}, 'topologie/topo.html', request) + return form( + {'topoform': constructor_switch, 'action_name': 'Editer'}, + 'topologie/topo.html', + request + ) @login_required @@ -778,10 +920,14 @@ def del_constructor_switch(request, constructor_switch, constructorswitchid): constructor_switch.delete() messages.success(request, "Le constructeur a été détruit") except ProtectedError: - messages.error(request, "Le constructeur %s est affecté à un autre objet,\ - impossible de la supprimer (switch ou user)" % constructor_switch) + messages.error( + request, + ("Le constructeur %s est affecté à un autre objet, impossible " + "de la supprimer (switch ou user)" % constructor_switch) + ) return redirect(reverse('topologie:index-model-switch')) - return form({ - 'objet': constructor_switch, - 'objet_name': 'Constructeur de switch' - }, 'topologie/delete.html', request) + return form( + {'objet': constructor_switch, 'objet_name': 'Constructeur de switch'}, + 'topologie/delete.html', + request + ) From 7b0b5c2af443dcae737ec015f3eec3728e46fb87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 01:25:05 +0000 Subject: [PATCH 08/22] Pep8 compliance on users --- users/acl.py | 1 + users/forms.py | 14 +- users/management/commands/chgpass.py | 15 +- users/management/commands/chsh.py | 25 +- .../management/commands/derniere_connexion.py | 12 +- users/management/commands/email.py | 7 +- users/management/commands/ldap_sync.py | 2 +- users/models.py | 232 ++++++++++++------ users/serializers.py | 4 +- users/urls.py | 100 +++----- users/views.py | 217 ++++++++++++---- 11 files changed, 416 insertions(+), 213 deletions(-) diff --git a/users/acl.py b/users/acl.py index 2e82b0f3..8eca1e63 100644 --- a/users/acl.py +++ b/users/acl.py @@ -26,6 +26,7 @@ Here are defined some functions to check acl on the application. """ + def can_view(user): """Check if an user can view the application. diff --git a/users/forms.py b/users/forms.py index 3617b26f..a43db749 100644 --- a/users/forms.py +++ b/users/forms.py @@ -89,12 +89,16 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): password1 = self.cleaned_data.get("passwd1") password2 = self.cleaned_data.get("passwd2") if password1 and password2 and password1 != password2: - raise forms.ValidationError("Les 2 nouveaux mots de passe sont différents") + raise forms.ValidationError( + "Les 2 nouveaux mots de passe sont différents" + ) return password2 def clean_selfpasswd(self): """Verifie si il y a lieu que le mdp self est correct""" - if not self.instance.check_password(self.cleaned_data.get("selfpasswd")): + if not self.instance.check_password( + self.cleaned_data.get("selfpasswd") + ): raise forms.ValidationError("Le mot de passe actuel est incorrect") return @@ -386,7 +390,11 @@ class ClubAdminandMembersForm(FormRevMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(ClubAdminandMembersForm, self).__init__(*args, prefix=prefix, **kwargs) + super(ClubAdminandMembersForm, self).__init__( + *args, + prefix=prefix, + **kwargs + ) class PasswordForm(FormRevMixin, ModelForm): diff --git a/users/management/commands/chgpass.py b/users/management/commands/chgpass.py index c3fabf8a..9763ae4c 100644 --- a/users/management/commands/chgpass.py +++ b/users/management/commands/chgpass.py @@ -19,12 +19,14 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import os, pwd +import os +import pwd from django.core.management.base import BaseCommand, CommandError from users.forms import PassForm from re2o.script_utils import get_user, get_system_user, form_cli + class Command(BaseCommand): help = "Changer le mot de passe d'un utilisateur" @@ -42,6 +44,13 @@ class Command(BaseCommand): if not ok: raise CommandError(msg) - self.stdout.write("Changement du mot de passe de %s" % target_user.pseudo) + self.stdout.write( + "Changement du mot de passe de %s" % target_user.pseudo + ) - form_cli(PassForm,current_user,"Changement du mot de passe",instance=target_user) + form_cli( + PassForm, + current_user, + "Changement du mot de passe", + instance=target_user + ) diff --git a/users/management/commands/chsh.py b/users/management/commands/chsh.py index 6c5b06f7..6921ad79 100644 --- a/users/management/commands/chsh.py +++ b/users/management/commands/chsh.py @@ -19,7 +19,9 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import os, sys, pwd +import os +import sys +import pwd from django.core.management.base import BaseCommand, CommandError from django.db import transaction @@ -28,6 +30,7 @@ from reversion import revisions as reversion from users.models import User, ListShell from re2o.script_utils import get_user, get_system_user + class Command(BaseCommand): help = 'Change the default shell of a user' @@ -36,13 +39,13 @@ class Command(BaseCommand): def handle(self, *args, **options): - current_username = get_system_user() + current_username = get_system_user() current_user = get_user(current_username) target_username = options["target_username"] or current_username target_user = get_user(target_username) - #L'utilisateur n'a pas le droit de changer le shell + # L'utilisateur n'a pas le droit de changer le shell ok, msg = target_user.can_change_shell(current_user) if not ok: raise CommandError(msg) @@ -52,9 +55,16 @@ class Command(BaseCommand): current_shell = "inconnu" if target_user.shell: current_shell = target_user.shell.get_pretty_name() - self.stdout.write("Choisissez un shell pour l'utilisateur %s (le shell actuel est %s) :" % (target_user.pseudo, current_shell)) + self.stdout.write( + "Choisissez un shell pour l'utilisateur %s (le shell actuel est " + "%s) :" % (target_user.pseudo, current_shell) + ) for shell in shells: - self.stdout.write("%d - %s (%s)" % (shell.id, shell.get_pretty_name(), shell.shell)) + self.stdout.write("%d - %s (%s)" % ( + shell.id, + shell.get_pretty_name(), + shell.shell + )) shell_id = input("Entrez un nombre : ") try: @@ -72,4 +82,7 @@ class Command(BaseCommand): reversion.set_user(current_user) reversion.set_comment("Shell modifié") - self.stdout.write(self.style.SUCCESS("Shell modifié. La modification peut prendre quelques minutes pour s'appliquer.")) + self.stdout.write(self.style.SUCCESS( + "Shell modifié. La modification peut prendre quelques minutes " + "pour s'appliquer." + )) diff --git a/users/management/commands/derniere_connexion.py b/users/management/commands/derniere_connexion.py index 94dbf939..d936fda8 100644 --- a/users/management/commands/derniere_connexion.py +++ b/users/management/commands/derniere_connexion.py @@ -4,7 +4,6 @@ # quelques clics. # # Copyright © 2018 Benjamin Graillot -# # Copyright © 2013-2015 Raphaël-David Lasseri # # This program is free software; you can redistribute it and/or modify @@ -32,7 +31,8 @@ from users.models import User # Une liste d'expressions régulières à chercher dans les logs. # Elles doivent contenir un groupe 'date' et un groupe 'user'. -# Pour le CAS on prend comme entrée cat ~/cas.log | grep -B 2 -A 2 "ACTION: AUTHENTICATION_SUCCESS"| grep 'WHEN\|WHO'|sed 'N;s/\n/ /' +# Pour le CAS on prend comme entrée +# cat ~/cas.log | grep -B 2 -A 2 "ACTION: AUTHENTICATION_SUCCESS"| grep 'WHEN\|WHO'|sed 'N;s/\n/ /' COMPILED_REGEX = map(re.compile, [ r'^(?P\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}).*(?:'r'dovecot.*Login: user=<|'r'sshd.*Accepted.*for 'r')(?P[^ >]+).*$', r'^(?P.*) LOGIN INFO User logged in : (?P.*)', @@ -48,8 +48,10 @@ DATE_FORMATS = [ "%a %b %d CEST %H:%M:%S%Y" ] + class Command(BaseCommand): - help = 'Update the time of the latest connection for users by matching stdin against a set of regular expressions' + help = ('Update the time of the latest connection for users by matching ' + 'stdin against a set of regular expressions') def handle(self, *args, **options): @@ -65,7 +67,9 @@ class Command(BaseCommand): for i, regex in enumerate(COMPILED_REGEX): m = regex.match(line) if m: - parsed_log[m.group('user')] = make_aware(datetime.strptime(m.group('date'), DATE_FORMATS[i])) + parsed_log[m.group('user')] = make_aware( + datetime.strptime(m.group('date'), DATE_FORMATS[i]) + ) return parsed_log parsed_log = parse_logs(sys.stdin) diff --git a/users/management/commands/email.py b/users/management/commands/email.py index a7518b9f..5e1d02c4 100644 --- a/users/management/commands/email.py +++ b/users/management/commands/email.py @@ -7,8 +7,11 @@ from users.models import User UTC = pytz.timezone('UTC') + +# TODO : remove of finsihed this because currently it should +# be failing! Who commited that ?! class Command(BaseCommand): - commands = ['email_remainder',] + commands = ['email_remainder'] args = '[command]' help = 'Send email remainders' @@ -27,6 +30,6 @@ class Command(BaseCommand): elif remaining.days == 1: last_day_reminder() + def month_reminder(): pass - diff --git a/users/management/commands/ldap_sync.py b/users/management/commands/ldap_sync.py index 7588ffc9..9301c788 100644 --- a/users/management/commands/ldap_sync.py +++ b/users/management/commands/ldap_sync.py @@ -20,6 +20,7 @@ from django.core.management.base import BaseCommand, CommandError from users.models import User + class Command(BaseCommand): help = 'Synchronise le ldap à partir du sql. A utiliser dans un cron' @@ -37,4 +38,3 @@ class Command(BaseCommand): def handle(self, *args, **options): for usr in User.objects.all(): usr.ldap_sync(mac_refresh=options['full']) - diff --git a/users/models.py b/users/models.py index bacd38cc..a0561aed 100644 --- a/users/models.py +++ b/users/models.py @@ -153,7 +153,7 @@ class UserManager(BaseUserManager): user.set_password(password) if su: - user.is_superuser=True + user.is_superuser = True user.save(using=self._db) return user @@ -171,7 +171,9 @@ class UserManager(BaseUserManager): """ return self._create_user(pseudo, surname, email, password, True) -class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin, AclMixin): + +class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, + PermissionsMixin, AclMixin): """ Definition de l'utilisateur de base. Champs principaux : name, surnname, pseudo, email, room, password Herite du django BaseUser et du système d'auth django""" @@ -219,7 +221,11 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix registered = models.DateTimeField(auto_now_add=True) telephone = models.CharField(max_length=15, blank=True, null=True) uid_number = models.PositiveIntegerField(default=auto_uid, unique=True) - rezo_rez_uid = models.PositiveIntegerField(unique=True, blank=True, null=True) + rezo_rez_uid = models.PositiveIntegerField( + unique=True, + blank=True, + null=True + ) USERNAME_FIELD = 'pseudo' REQUIRED_FIELDS = ['surname', 'email'] @@ -228,13 +234,18 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix class Meta: permissions = ( - ("change_user_password", "Peut changer le mot de passe d'un user"), + ("change_user_password", + "Peut changer le mot de passe d'un user"), ("change_user_state", "Peut éditer l'etat d'un user"), ("change_user_force", "Peut forcer un déménagement"), ("change_user_shell", "Peut éditer le shell d'un user"), - ("change_user_groups", "Peut éditer les groupes d'un user ! Permission critique"), - ("change_all_users", "Peut éditer tous les users, y compris ceux dotés de droits. Superdroit"), - ("view_user", "Peut voir un objet user quelquonque"), + ("change_user_groups", + "Peut éditer les groupes d'un user ! Permission critique"), + ("change_all_users", + "Peut éditer tous les users, y compris ceux dotés de droits. " + "Superdroit"), + ("view_user", + "Peut voir un objet user quelquonque"), ) @cached_property @@ -286,7 +297,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix @property def is_admin(self): """ Renvoie si l'user est admin""" - admin,_ = Group.objects.get_or_create(name="admin") + admin, _ = Group.objects.get_or_create(name="admin") return self.is_superuser or admin in self.groups.all() def get_full_name(self): @@ -393,8 +404,9 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix def has_access(self): """ Renvoie si un utilisateur a accès à internet """ - return self.state == User.STATE_ACTIVE\ - and not self.is_ban() and (self.is_connected() or self.is_whitelisted()) + return (self.state == User.STATE_ACTIVE and + not self.is_ban() and + (self.is_connected() or self.is_whitelisted())) def end_access(self): """ Renvoie la date de fin normale d'accès (adhésion ou whiteliste)""" @@ -480,7 +492,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix self.assign_ips() self.state = User.STATE_ACTIVE - def ldap_sync(self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False): + def ldap_sync(self, base=True, access_refresh=True, mac_refresh=True, + group_refresh=False): """ Synchronisation du ldap. Synchronise dans le ldap les attributs de self Options : base : synchronise tous les attributs de base - nom, prenom, @@ -573,12 +586,15 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix 'asso_mail': AssoOption.get_cached_value('contact'), 'site_name': GeneralOption.get_cached_value('site_name'), 'url': request.build_absolute_uri( - reverse('users:process', kwargs={'token': req.token})), - 'expire_in': str(GeneralOption.get_cached_value('req_expire_hrs')) + ' heures', - } + reverse('users:process', kwargs={'token': req.token}) + ), + 'expire_in': str( + GeneralOption.get_cached_value('req_expire_hrs') + ) + ' heures', + } send_mail( - 'Changement de mot de passe du %(name)s / Password\ - renewal for %(name)s' % {'name': AssoOption.get_cached_value('name')}, + 'Changement de mot de passe du %(name)s / Password renewal for ' + '%(name)s' % {'name': AssoOption.get_cached_value('name')}, template.render(context), GeneralOption.get_cached_value('email_from'), [req.user.email], @@ -590,7 +606,9 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix """ Fonction appellée par freeradius. Enregistre la mac pour une machine inconnue sur le compte de l'user""" all_interfaces = self.user_interfaces(active=False) - if all_interfaces.count() > OptionalMachine.get_cached_value('max_lambdauser_interfaces'): + if all_interfaces.count() > OptionalMachine.get_cached_value( + 'max_lambdauser_interfaces' + ): return False, "Maximum de machines enregistrees atteinte" if not nas_type: return False, "Re2o ne sait pas à quel machinetype affecter cette\ @@ -625,9 +643,9 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix template = loader.get_template('users/email_auto_newmachine') context = Context({ 'nom': self.get_full_name(), - 'mac_address' : interface.mac_address, + 'mac_address': interface.mac_address, 'asso_name': AssoOption.get_cached_value('name'), - 'interface_name' : interface.domain, + 'interface_name': interface.domain, 'asso_email': AssoOption.get_cached_value('contact'), 'pseudo': self.pseudo, }) @@ -674,13 +692,13 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix :param self: The user which is to be edited. :param user_request: The user who requests to edit self. :return: a message and a boolean which is True if self is a club and - user_request one of its member, or if user_request is self, or if - user_request has the 'cableur' right. + user_request one of its member, or if user_request is self, or if + user_request has the 'cableur' right. """ if self.is_class_club and user_request.is_class_adherent: - if self == user_request or \ - user_request.has_perm('users.change_user') or \ - user_request.adherent in self.club.administrators.all(): + if (self == user_request or + user_request.has_perm('users.change_user') or + user_request.adherent in self.club.administrators.all()): return True, None else: return False, u"Vous n'avez pas le droit d'éditer ce club" @@ -691,52 +709,70 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix return True, None elif user_request.has_perm('users.change_user'): if self.groups.filter(listright__critical=True): - return False, u"Utilisateurs avec droits critiques, ne peut etre édité" + return False, (u"Utilisateurs avec droits critiques, ne " + "peut etre édité") elif self == AssoOption.get_cached_value('utilisateur_asso'): - return False, u"Impossible d'éditer l'utilisateur asso sans droit change_all_users" + return False, (u"Impossible d'éditer l'utilisateur asso " + "sans droit change_all_users") else: return True, None elif user_request.has_perm('users.change_all_users'): return True, None else: - return False, u"Vous ne pouvez éditer un autre utilisateur que vous même" + return False, (u"Vous ne pouvez éditer un autre utilisateur " + "que vous même") def can_change_password(self, user_request, *args, **kwargs): if self.is_class_club and user_request.is_class_adherent: - if self == user_request or \ - user_request.has_perm('users.change_user_password') or \ - user_request.adherent in self.club.administrators.all(): + if (self == user_request or + user_request.has_perm('users.change_user_password') or + user_request.adherent in self.club.administrators.all()): return True, None else: return False, u"Vous n'avez pas le droit d'éditer ce club" else: - if self == user_request or \ - user_request.has_perm('users.change_user_groups'): - # Peut éditer les groupes d'un user, c'est un privilège élevé, True + if (self == user_request or + user_request.has_perm('users.change_user_groups')): + # Peut éditer les groupes d'un user, + # c'est un privilège élevé, True return True, None - elif user_request.has_perm('users.change_user') and not self.groups.all(): + elif (user_request.has_perm('users.change_user') and + not self.groups.all()): return True, None else: - return False, u"Vous ne pouvez éditer un autre utilisateur que vous même" + return False, (u"Vous ne pouvez éditer un autre utilisateur " + "que vous même") def check_selfpasswd(self, user_request, *args, **kwargs): return user_request == self, None @staticmethod def can_change_state(user_request, *args, **kwargs): - return user_request.has_perm('users.change_user_state'), "Droit requis pour changer l'état" + return ( + user_request.has_perm('users.change_user_state'), + "Droit requis pour changer l'état" + ) @staticmethod def can_change_shell(user_request, *args, **kwargs): - return user_request.has_perm('users.change_user_shell'), "Droit requis pour changer le shell" + return ( + user_request.has_perm('users.change_user_shell'), + "Droit requis pour changer le shell" + ) @staticmethod def can_change_force(user_request, *args, **kwargs): - return user_request.has_perm('users.change_user_force'), "Droit requis pour forcer le déménagement" + return ( + user_request.has_perm('users.change_user_force'), + "Droit requis pour forcer le déménagement" + ) @staticmethod def can_change_groups(user_request, *args, **kwargs): - return user_request.has_perm('users.change_user_groups'), "Droit requis pour éditer les groupes de l'user" + return ( + user_request.has_perm('users.change_user_groups'), + "Droit requis pour éditer les groupes de l'user" + ) def can_view(self, user_request, *args, **kwargs): """Check if an user can view an user object. @@ -744,45 +780,55 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix :param self: The targeted user. :param user_request: The user who ask for viewing the target. :return: A boolean telling if the acces is granted and an explanation - text + text """ if self.is_class_club and user_request.is_class_adherent: - if self == user_request or \ - user_request.has_perm('users.view_user') or \ - user_request.adherent in self.club.administrators.all() or \ - user_request.adherent in self.club.members.all(): + if (self == user_request or + user_request.has_perm('users.view_user') or + user_request.adherent in self.club.administrators.all() or + user_request.adherent in self.club.members.all()): return True, None else: return False, u"Vous n'avez pas le droit de voir ce club" else: - if self == user_request or user_request.has_perm('users.view_user'): + if (self == user_request or + user_request.has_perm('users.view_user')): return True, None else: - return False, u"Vous ne pouvez voir un autre utilisateur que vous même" + return False, (u"Vous ne pouvez voir un autre utilisateur " + "que vous même") def can_view_all(user_request, *args, **kwargs): """Check if an user can access to the list of every user objects :param user_request: The user who wants to view the list. - :return: True if the user can view the list and an explanation message. + :return: True if the user can view the list and an explanation + message. """ - return user_request.has_perm('users.view_user'), u"Vous n'avez pas accès à la liste des utilisateurs." + return ( + user_request.has_perm('users.view_user'), + u"Vous n'avez pas accès à la liste des utilisateurs." + ) def can_delete(self, user_request, *args, **kwargs): """Check if an user can delete an user object. :param self: The user who is to be deleted. :param user_request: The user who requests deletion. - :return: True if user_request has the right 'bureau', and a message. + :return: True if user_request has the right 'bureau', and a + message. """ - return user_request.has_perm('users.delete_user'), u"Vous ne pouvez pas supprimer cet utilisateur." + return ( + user_request.has_perm('users.delete_user'), + u"Vous ne pouvez pas supprimer cet utilisateur." + ) def __init__(self, *args, **kwargs): super(User, self).__init__(*args, **kwargs) self.field_permissions = { - 'shell' : self.can_change_shell, - 'force' : self.can_change_force, - 'selfpasswd' : self.check_selfpasswd, + 'shell': self.can_change_shell, + 'force': self.can_change_force, + 'selfpasswd': self.check_selfpasswd, } def __str__(self): @@ -812,16 +858,20 @@ class Adherent(User): :param user_request: The user who wants to create a user object. :return: a message and a boolean which is True if the user can create - an user or if the `options.all_can_create` is set. + a user or if the `options.all_can_create` is set. """ - if(not user_request.is_authenticated and not OptionalUser.get_cached_value('self_adhesion')): + if (not user_request.is_authenticated and + not OptionalUser.get_cached_value('self_adhesion')): return False, None else: - if(OptionalUser.get_cached_value('all_can_create_adherent') or OptionalUser.get_cached_value('self_adhesion')): + if (OptionalUser.get_cached_value('all_can_create_adherent') or + OptionalUser.get_cached_value('self_adhesion')): return True, None else: - return user_request.has_perm('users.add_user'), u"Vous n'avez pas le\ - droit de créer un utilisateur" + return ( + user_request.has_perm('users.add_user'), + u"Vous n'avez pas le droit de créer un utilisateur" + ) class Club(User): @@ -843,7 +893,7 @@ class Club(User): related_name='club_members' ) mailing = models.BooleanField( - default = False + default=False ) def can_create(user_request, *args, **kwargs): @@ -851,7 +901,7 @@ class Club(User): :param user_request: The user who wants to create a user object. :return: a message and a boolean which is True if the user can create - an user or if the `options.all_can_create` is set. + an user or if the `options.all_can_create` is set. """ if not user_request.is_authenticated: return False, None @@ -859,19 +909,24 @@ class Club(User): if OptionalUser.get_cached_value('all_can_create_club'): return True, None else: - return user_request.has_perm('users.add_user'), u"Vous n'avez pas le\ - droit de créer un club" + return ( + user_request.has_perm('users.add_user'), + u"Vous n'avez pas le droit de créer un club" + ) def can_view_all(user_request, *args, **kwargs): """Check if an user can access to the list of every user objects :param user_request: The user who wants to view the list. - :return: True if the user can view the list and an explanation message. + :return: True if the user can view the list and an explanation + message. """ if user_request.has_perm('users.view_user'): return True, None - if hasattr(user_request,'is_class_adherent') and user_request.is_class_adherent: - if user_request.adherent.club_administrator.all() or user_request.adherent.club_members.all(): + if (hasattr(user_request, 'is_class_adherent') and + user_request.is_class_adherent): + if (user_request.adherent.club_administrator.all() or + user_request.adherent.club_members.all()): return True, None return False, u"Vous n'avez pas accès à la liste des utilisateurs." @@ -892,9 +947,15 @@ def user_post_save(sender, **kwargs): Synchronise le ldap""" is_created = kwargs['created'] user = kwargs['instance'] - #if is_created: - #user.notif_inscription() - user.ldap_sync(base=True, access_refresh=True, mac_refresh=False, group_refresh=True) + # TODO : remove if unnecessary + # if is_created: + # user.notif_inscription() + user.ldap_sync( + base=True, + access_refresh=True, + mac_refresh=False, + group_refresh=True + ) regen('mailing') @@ -907,6 +968,7 @@ def user_post_delete(sender, **kwargs): user.ldap_del() regen('mailing') + class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): """ Classe des users daemons, règle leurs accès au ldap""" readonly = 'readonly' @@ -977,6 +1039,7 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): def __str__(self): return self.pseudo + @receiver(post_save, sender=ServiceUser) def service_user_post_save(sender, **kwargs): """ Synchronise un service user ldap après modification django""" @@ -1019,8 +1082,8 @@ class ListRight(RevMixin, AclMixin, Group): unique=True, validators=[RegexValidator( '^[a-z]+$', - message="Les groupes unix ne peuvent contenir\ - que des lettres minuscules" + message=("Les groupes unix ne peuvent contenir que des lettres " + "minuscules") )] ) gid = models.PositiveIntegerField(unique=True, null=True) @@ -1148,10 +1211,10 @@ class Ban(RevMixin, AclMixin, models.Model): :return: A boolean telling if the acces is granted and an explanation text """ - if not user_request.has_perm('users.view_ban') and\ - self.user != user_request: - return False, u"Vous n'avez pas le droit de voir les bannissements\ - autre que les vôtres" + if (not user_request.has_perm('users.view_ban') and + self.user != user_request): + return False, (u"Vous n'avez pas le droit de voir les " + "bannissements autre que les vôtres") else: return True, None @@ -1213,10 +1276,10 @@ class Whitelist(RevMixin, AclMixin, models.Model): :return: A boolean telling if the acces is granted and an explanation text """ - if not user_request.has_perm('users.view_whitelist') and\ - self.user != user_request: - return False, u"Vous n'avez pas le droit de voir les accès\ - gracieux autre que les vôtres" + if (not user_request.has_perm('users.view_whitelist') and + self.user != user_request): + return False, (u"Vous n'avez pas le droit de voir les accès " + "gracieux autre que les vôtres") else: return True, None @@ -1270,8 +1333,12 @@ class Request(models.Model): def save(self): if not self.expires_at: - self.expires_at = timezone.now() \ - + datetime.timedelta(hours=GeneralOption.get_cached_value('req_expire_hrs')) + self.expires_at = (timezone.now() + + datetime.timedelta( + hours=GeneralOption.get_cached_value( + 'req_expire_hrs' + ) + )) if not self.token: self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens super(Request, self).save() @@ -1375,7 +1442,10 @@ class LdapUserGroup(ldapdb.models.Model): # attributes gid = ldapdb.models.fields.IntegerField(db_column='gidNumber') - members = ldapdb.models.fields.ListField(db_column='memberUid', blank=True) + members = ldapdb.models.fields.ListField( + db_column='memberUid', + blank=True + ) name = ldapdb.models.fields.CharField( db_column='cn', max_length=200, diff --git a/users/serializers.py b/users/serializers.py index 95388d41..d2f66abe 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -20,11 +20,12 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -#Maël Kervella +# Maël Kervella from rest_framework import serializers from users.models import Club, Adherent + class MailingSerializer(serializers.ModelSerializer): name = serializers.CharField(source='pseudo') @@ -32,6 +33,7 @@ class MailingSerializer(serializers.ModelSerializer): model = Club fields = ('name',) + class MailingMemberSerializer(serializers.ModelSerializer): class Meta: model = Adherent diff --git a/users/urls.py b/users/urls.py index b8f428b0..05f72be0 100644 --- a/users/urls.py +++ b/users/urls.py @@ -34,109 +34,79 @@ urlpatterns = [ url(r'^new_user/$', views.new_user, name='new-user'), url(r'^new_club/$', views.new_club, name='new-club'), url(r'^edit_info/(?P[0-9]+)$', views.edit_info, name='edit-info'), - url( - r'^edit_club_admin_members/(?P[0-9]+)$', + url(r'^edit_club_admin_members/(?P[0-9]+)$', views.edit_club_admin_members, - name='edit-club-admin-members' - ), + name='edit-club-admin-members'), url(r'^state/(?P[0-9]+)$', views.state, name='state'), url(r'^groups/(?P[0-9]+)$', views.groups, name='groups'), url(r'^password/(?P[0-9]+)$', views.password, name='password'), - url(r'^del_group/(?P[0-9]+)/(?P[0-9]+)$', views.del_group, name='del-group'), + url(r'^del_group/(?P[0-9]+)/(?P[0-9]+)$', + views.del_group, + name='del-group'), url(r'^new_serviceuser/$', views.new_serviceuser, name='new-serviceuser'), - url( - r'^edit_serviceuser/(?P[0-9]+)$', + url(r'^edit_serviceuser/(?P[0-9]+)$', views.edit_serviceuser, - name='edit-serviceuser' - ), - url( - r'^del_serviceuser/(?P[0-9]+)$', + name='edit-serviceuser'), + url(r'^del_serviceuser/(?P[0-9]+)$', views.del_serviceuser, - name='del-serviceuser' - ), + name='del-serviceuser'), url(r'^add_ban/(?P[0-9]+)$', views.add_ban, name='add-ban'), url(r'^edit_ban/(?P[0-9]+)$', views.edit_ban, name='edit-ban'), - url( - r'^add_whitelist/(?P[0-9]+)$', + url(r'^add_whitelist/(?P[0-9]+)$', views.add_whitelist, - name='add-whitelist' - ), - url( - r'^edit_whitelist/(?P[0-9]+)$', + name='add-whitelist'), + url(r'^edit_whitelist/(?P[0-9]+)$', views.edit_whitelist, - name='edit-whitelist' - ), + name='edit-whitelist'), url(r'^add_school/$', views.add_school, name='add-school'), - url( - r'^edit_school/(?P[0-9]+)$', + url(r'^edit_school/(?P[0-9]+)$', views.edit_school, - name='edit-school' - ), + name='edit-school'), url(r'^del_school/$', views.del_school, name='del-school'), url(r'^add_listright/$', views.add_listright, name='add-listright'), - url( - r'^edit_listright/(?P[0-9]+)$', + url(r'^edit_listright/(?P[0-9]+)$', views.edit_listright, - name='edit-listright' - ), + name='edit-listright'), url(r'^del_listright/$', views.del_listright, name='del-listright'), url(r'^add_shell/$', views.add_shell, name='add-shell'), - url( - r'^edit_shell/(?P[0-9]+)$', + url(r'^edit_shell/(?P[0-9]+)$', views.edit_shell, - name='edit-shell' - ), - url( - r'^del_shell/(?P[0-9]+)$', + name='edit-shell'), + url(r'^del_shell/(?P[0-9]+)$', views.del_shell, - name='del-shell' - ), + name='del-shell'), url(r'^profil/(?P[0-9]+)$', views.profil, name='profil'), url(r'^index_ban/$', views.index_ban, name='index-ban'), url(r'^index_white/$', views.index_white, name='index-white'), url(r'^index_school/$', views.index_school, name='index-school'), url(r'^index_shell/$', views.index_shell, name='index-shell'), url(r'^index_listright/$', views.index_listright, name='index-listright'), - url( - r'^index_serviceusers/$', + url(r'^index_serviceusers/$', views.index_serviceusers, - name='index-serviceusers' - ), + name='index-serviceusers'), url(r'^mon_profil/$', views.mon_profil, name='mon-profil'), url(r'^process/(?P[a-z0-9]{32})/$', views.process, name='process'), url(r'^reset_password/$', views.reset_password, name='reset-password'), url(r'^mass_archive/$', views.mass_archive, name='mass-archive'), - url( - r'^history/(?P\w+)/(?P[0-9]+)$', + url(r'^history/(?P\w+)/(?P[0-9]+)$', re2o.views.history, name='history', - kwargs={'application':'users'}, - ), + kwargs={'application': 'users'}), url(r'^$', views.index, name='index'), url(r'^index_clubs/$', views.index_clubs, name='index-clubs'), - url( - r'^rest/ml/std/$', + url(r'^rest/ml/std/$', views.ml_std_list, - name='ml-std-list' - ), - url( - r'^rest/ml/std/member/(?P\w+)/$', + name='ml-std-list'), + url(r'^rest/ml/std/member/(?P\w+)/$', views.ml_std_members, - name='ml-std-members' - ), - url( - r'^rest/ml/club/$', + name='ml-std-members'), + url(r'^rest/ml/club/$', views.ml_club_list, - name='ml-club-list' - ), - url( - r'^rest/ml/club/admin/(?P\w+)/$', + name='ml-club-list'), + url(r'^rest/ml/club/admin/(?P\w+)/$', views.ml_club_admins, - name='ml-club-admins' - ), - url( - r'^rest/ml/club/member/(?P\w+)/$', + name='ml-club-admins'), + url(r'^rest/ml/club/member/(?P\w+)/$', views.ml_club_members, - name='ml-club-members' - ), + name='ml-club-members'), ] diff --git a/users/views.py b/users/views.py index b0cfb000..d82d5563 100644 --- a/users/views.py +++ b/users/views.py @@ -106,6 +106,7 @@ from re2o.acl import ( can_change ) + @can_create(Adherent) def new_user(request): """ Vue de création d'un nouvel utilisateur, @@ -121,9 +122,19 @@ def new_user(request): pour l'initialisation du mot de passe a été envoyé" % user.pseudo) return redirect(reverse( 'users:profil', - kwargs={'userid':str(user.id)} - )) - return form({'userform': user,'GTU_sum_up':GTU_sum_up,'GTU':GTU,'showCGU':True, 'action_name':'Créer un utilisateur'}, 'users/user.html', request) + kwargs={'userid': str(user.id)} + )) + return form( + { + 'userform': user, + 'GTU_sum_up': GTU_sum_up, + 'GTU': GTU, + 'showCGU': True, + 'action_name': 'Créer un utilisateur' + }, + 'users/user.html', + request + ) @login_required @@ -140,9 +151,13 @@ def new_club(request): pour l'initialisation du mot de passe a été envoyé" % club.pseudo) return redirect(reverse( 'users:profil', - kwargs={'userid':str(club.id)} - )) - return form({'userform': club, 'showCGU':False, 'action_name':'Créer un club'}, 'users/user.html', request) + kwargs={'userid': str(club.id)} + )) + return form( + {'userform': club, 'showCGU': False, 'action_name': 'Créer un club'}, + 'users/user.html', + request + ) @login_required @@ -150,16 +165,27 @@ def new_club(request): def edit_club_admin_members(request, club_instance, clubid): """Vue d'edition de la liste des users administrateurs et membres d'un club""" - club = ClubAdminandMembersForm(request.POST or None, instance=club_instance) + club = ClubAdminandMembersForm( + request.POST or None, + instance=club_instance + ) if club.is_valid(): if club.changed_data: club.save() messages.success(request, "Le club a bien été modifié") return redirect(reverse( 'users:profil', - kwargs={'userid':str(club_instance.id)} - )) - return form({'userform': club, 'showCGU':False, 'action_name':'Editer les admin et membres'}, 'users/user.html', request) + kwargs={'userid': str(club_instance.id)} + )) + return form( + { + 'userform': club, + 'showCGU': False, + 'action_name': 'Editer les admin et membres' + }, + 'users/user.html', + request + ) @login_required @@ -186,9 +212,13 @@ def edit_info(request, user, userid): messages.success(request, "L'user a bien été modifié") return redirect(reverse( 'users:profil', - kwargs={'userid':str(userid)} - )) - return form({'userform': user, 'action_name': "Editer l'utilisateur"}, 'users/user.html', request) + kwargs={'userid': str(userid)} + )) + return form( + {'userform': user, 'action_name': "Editer l'utilisateur"}, + 'users/user.html', + request + ) @login_required @@ -207,9 +237,13 @@ def state(request, user, userid): messages.success(request, "Etat changé avec succès") return redirect(reverse( 'users:profil', - kwargs={'userid':str(userid)} - )) - return form({'userform': state, 'action_name': "Editer l'état"}, 'users/user.html', request) + kwargs={'userid': str(userid)} + )) + return form( + {'userform': state, 'action_name': "Editer l'état"}, + 'users/user.html', + request + ) @login_required @@ -222,9 +256,13 @@ def groups(request, user, userid): messages.success(request, "Groupes changés avec succès") return redirect(reverse( 'users:profil', - kwargs={'userid':str(userid)} + kwargs={'userid': str(userid)} )) - return form({'userform': group, 'action_name':'Editer les groupes'}, 'users/user.html', request) + return form( + {'userform': group, 'action_name': 'Editer les groupes'}, + 'users/user.html', + request + ) @login_required @@ -239,10 +277,14 @@ def password(request, user, userid): u_form.save() messages.success(request, "Le mot de passe a changé") return redirect(reverse( - 'users:profil', - kwargs={'userid':str(user.id)} + 'users:profil', + kwargs={'userid': str(user.id)} )) - return form({'userform': u_form, 'action_name':'Changer le mot de passe'}, 'users/user.html', request) + return form( + {'userform': u_form, 'action_name': 'Changer le mot de passe'}, + 'users/user.html', + request + ) @login_required @@ -268,14 +310,21 @@ def new_serviceuser(request): "L'utilisateur %s a été crée" % user_object.pseudo ) return redirect(reverse('users:index-serviceusers')) - return form({'userform': user, 'action_name':'Créer un serviceuser'}, 'users/user.html', request) + return form( + {'userform': user, 'action_name': 'Créer un serviceuser'}, + 'users/user.html', + request + ) @login_required @can_edit(ServiceUser) def edit_serviceuser(request, serviceuser, serviceuserid): """ Edit a ServiceUser """ - serviceuser = EditServiceUserForm(request.POST or None, instance=serviceuser) + serviceuser = EditServiceUserForm( + request.POST or None, + instance=serviceuser + ) if serviceuser.is_valid(): user_object = serviceuser.save(commit=False) if serviceuser.cleaned_data['password']: @@ -284,7 +333,11 @@ def edit_serviceuser(request, serviceuser, serviceuserid): user_object.save() messages.success(request, "L'user a bien été modifié") return redirect(reverse('users:index-serviceusers')) - return form({'userform': serviceuser, 'action_name':'Editer un serviceuser'}, 'users/user.html', request) + return form( + {'userform': serviceuser, 'action_name': 'Editer un serviceuser'}, + 'users/user.html', + request + ) @login_required @@ -316,14 +369,19 @@ def add_ban(request, user, userid): messages.success(request, "Bannissement ajouté") return redirect(reverse( 'users:profil', - kwargs={'userid':str(userid)} + kwargs={'userid': str(userid)} )) if user.is_ban(): messages.error( request, "Attention, cet utilisateur a deja un bannissement actif" ) - return form({'userform': ban, 'action_name': 'Ajouter un ban'}, 'users/user.html', request) + return form( + {'userform': ban, 'action_name': 'Ajouter un ban'}, + 'users/user.html', + request + ) + @login_required @can_edit(Ban) @@ -337,7 +395,11 @@ def edit_ban(request, ban_instance, banid): ban.save() messages.success(request, "Bannissement modifié") return redirect(reverse('users:index')) - return form({'userform': ban, 'action_name': 'Editer un ban'}, 'users/user.html', request) + return form( + {'userform': ban, 'action_name': 'Editer un ban'}, + 'users/user.html', + request + ) @login_required @@ -358,14 +420,18 @@ def add_whitelist(request, user, userid): messages.success(request, "Accès à titre gracieux accordé") return redirect(reverse( 'users:profil', - kwargs={'userid':str(userid)} - )) + kwargs={'userid': str(userid)} + )) if user.is_whitelisted(): messages.error( request, "Attention, cet utilisateur a deja un accès gracieux actif" ) - return form({'userform': whitelist, 'action_name': 'Ajouter une whitelist'}, 'users/user.html', request) + return form( + {'userform': whitelist, 'action_name': 'Ajouter une whitelist'}, + 'users/user.html', + request + ) @login_required @@ -384,7 +450,11 @@ def edit_whitelist(request, whitelist_instance, whitelistid): whitelist.save() messages.success(request, "Whitelist modifiée") return redirect(reverse('users:index')) - return form({'userform': whitelist, 'action_name': 'Editer une whitelist'}, 'users/user.html', request) + return form( + {'userform': whitelist, 'action_name': 'Editer une whitelist'}, + 'users/user.html', + request + ) @login_required @@ -397,7 +467,11 @@ def add_school(request): school.save() messages.success(request, "L'établissement a été ajouté") return redirect(reverse('users:index-school')) - return form({'userform': school, 'action_name':'Ajouter'}, 'users/user.html', request) + return form( + {'userform': school, 'action_name': 'Ajouter'}, + 'users/user.html', + request + ) @login_required @@ -411,7 +485,11 @@ def edit_school(request, school_instance, schoolid): school.save() messages.success(request, "Établissement modifié") return redirect(reverse('users:index-school')) - return form({'userform': school, 'action_name':'Editer'}, 'users/user.html', request) + return form( + {'userform': school, 'action_name': 'Editer'}, + 'users/user.html', + request + ) @login_required @@ -434,7 +512,11 @@ def del_school(request, instances): "L'établissement %s est affecté à au moins un user, \ vous ne pouvez pas le supprimer" % school_del) return redirect(reverse('users:index-school')) - return form({'userform': school, 'action_name': 'Supprimer'}, 'users/user.html', request) + return form( + {'userform': school, 'action_name': 'Supprimer'}, + 'users/user.html', + request + ) @login_required @@ -446,7 +528,11 @@ def add_shell(request): shell.save() messages.success(request, "Le shell a été ajouté") return redirect(reverse('users:index-shell')) - return form({'userform': shell, 'action_name':'Ajouter'}, 'users/user.html', request) + return form( + {'userform': shell, 'action_name': 'Ajouter'}, + 'users/user.html', + request + ) @login_required @@ -459,7 +545,11 @@ def edit_shell(request, shell_instance, listshellid): shell.save() messages.success(request, "Le shell a été modifié") return redirect(reverse('users:index-shell')) - return form({'userform': shell, 'action_name':'Editer'}, 'users/user.html', request) + return form( + {'userform': shell, 'action_name': 'Editer'}, + 'users/user.html', + request + ) @login_required @@ -487,7 +577,11 @@ def add_listright(request): listright.save() messages.success(request, "Le droit/groupe a été ajouté") return redirect(reverse('users:index-listright')) - return form({'userform': listright, 'action_name': 'Ajouter'}, 'users/user.html', request) + return form( + {'userform': listright, 'action_name': 'Ajouter'}, + 'users/user.html', + request + ) @login_required @@ -504,7 +598,11 @@ def edit_listright(request, listright_instance, listrightid): listright.save() messages.success(request, "Droit modifié") return redirect(reverse('users:index-listright')) - return form({'userform': listright, 'action_name': 'Editer'}, 'users/user.html', request) + return form( + {'userform': listright, 'action_name': 'Editer'}, + 'users/user.html', + request + ) @login_required @@ -525,7 +623,11 @@ def del_listright(request, instances): "Le groupe %s est affecté à au moins un user, \ vous ne pouvez pas le supprimer" % listright_del) return redirect(reverse('users:index-listright')) - return form({'userform': listright, 'action_name': 'Supprimer'}, 'users/user.html', request) + return form( + {'userform': listright, 'action_name': 'Supprimer'}, + 'users/user.html', + request + ) @login_required @@ -587,7 +689,11 @@ def index_clubs(request): SortTable.USERS_INDEX ) clubs_list = re2o_paginator(request, clubs_list, pagination_number) - return render(request, 'users/index_clubs.html', {'clubs_list': clubs_list}) + return render( + request, + 'users/index_clubs.html', + {'clubs_list': clubs_list} + ) @login_required @@ -688,7 +794,7 @@ def mon_profil(request): """ Lien vers profil, renvoie request.id à la fonction """ return redirect(reverse( 'users:profil', - kwargs={'userid':str(request.user.id)} + kwargs={'userid': str(request.user.id)} )) @@ -707,7 +813,9 @@ def profil(request, users, userid): request.GET.get('order'), SortTable.MACHINES_INDEX ) - pagination_large_number = GeneralOption.get_cached_value('pagination_large_number') + pagination_large_number = GeneralOption.get_cached_value( + 'pagination_large_number' + ) machines = re2o_paginator(request, machines, pagination_large_number) factures = Facture.objects.filter(user=users) factures = SortTable.sort( @@ -742,7 +850,7 @@ def profil(request, users, userid): 'ban_list': bans, 'white_list': whitelists, 'user_solde': user_solde, - 'allow_online_payment' : allow_online_payment, + 'allow_online_payment': allow_online_payment, } ) @@ -758,12 +866,20 @@ def reset_password(request): ) except User.DoesNotExist: messages.error(request, "Cet utilisateur n'existe pas") - return form({'userform': userform, 'action_name': 'Réinitialiser'}, 'users/user.html', request) + return form( + {'userform': userform, 'action_name': 'Réinitialiser'}, + 'users/user.html', + request + ) user.reset_passwd_mail(request) messages.success(request, "Un mail pour l'initialisation du mot\ de passe a été envoyé") redirect(reverse('index')) - return form({'userform': userform, 'action_name': 'Réinitialiser'}, 'users/user.html', request) + return form( + {'userform': userform, 'action_name': 'Réinitialiser'}, + 'users/user.html', + request + ) def process(request, token): @@ -790,7 +906,11 @@ def process_passwd(request, req): req.delete() messages.success(request, "Le mot de passe a changé") return redirect(reverse('index')) - return form({'userform': u_form, 'action_name': 'Changer le mot de passe'}, 'users/user.html', request) + return form( + {'userform': u_form, 'action_name': 'Changer le mot de passe'}, + 'users/user.html', + request + ) class JSONResponse(HttpResponse): @@ -862,6 +982,9 @@ def ml_club_members(request, ml_name): except Club.DoesNotExist: messages.error(request, "Cette mailing n'existe pas") return redirect(reverse('index')) - members = club.administrators.all().values('email').distinct() | club.members.all().values('email').distinct() + members = ( + club.administrators.all().values('email').distinct() | + club.members.all().values('email').distinct() + ) seria = MailingMemberSerializer(members, many=True) return JSONResponse(seria.data) From 955bf42b025ff609690c530c16aab087e5d32f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 01:53:34 +0000 Subject: [PATCH 09/22] Missing Fixes --- cotisations/payment_utils/comnpay.py | 4 ++-- machines/models.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cotisations/payment_utils/comnpay.py b/cotisations/payment_utils/comnpay.py index dcf7e22a..ad0d8c77 100644 --- a/cotisations/payment_utils/comnpay.py +++ b/cotisations/payment_utils/comnpay.py @@ -62,8 +62,8 @@ class Payment(): ret = "" for key in array_tpe: ret += ''.format( - 'k'=key, - 'v'=array_type[key] + k=key, + v=array_type[key] ) return ret diff --git a/machines/models.py b/machines/models.py index 361c7bc1..e3024578 100644 --- a/machines/models.py +++ b/machines/models.py @@ -592,7 +592,7 @@ class Mx(RevMixin, AclMixin, models.Model): fichiers de zones""" return "@ IN MX {prior} {name}".format( prior=str(self.priority).ljust(3), - name=str(name) + name=str(self.name) ) def __str__(self): From cc4401ff20d39ebdd3a0c908c63681f3d6aee676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 11:49:39 +0000 Subject: [PATCH 10/22] Pylint compliance on api --- api/serializers.py | 166 ++++++++++++++++++++++++++++++++++----------- api/tests.py | 5 +- api/utils.py | 4 ++ api/views.py | 70 ++++++++++++------- 4 files changed, 181 insertions(+), 64 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index 8e4c49aa..f2604068 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -29,20 +29,20 @@ from machines.models import ( IpType, Extension, IpList, - MachineType, Domain, Txt, Mx, Srv, Service_link, Ns, - OuverturePortList, OuverturePort, Ipv6List ) class ServiceLinkSerializer(serializers.ModelSerializer): + """ Serializer for the ServiceLink objects """ + name = serializers.CharField(source='service.service_type') class Meta: @@ -51,6 +51,8 @@ class ServiceLinkSerializer(serializers.ModelSerializer): class MailingSerializer(serializers.ModelSerializer): + """ Serializer to build Mailing objects """ + name = serializers.CharField(source='pseudo') class Meta: @@ -59,20 +61,27 @@ class MailingSerializer(serializers.ModelSerializer): class MailingMemberSerializer(serializers.ModelSerializer): + """ Serializer fot the Adherent objects (who belong to a + Mailing) """ + class Meta: model = Adherent - fields = ('email', 'name', 'surname', 'pseudo',) + fields = ('email',) class IpTypeField(serializers.RelatedField): - """Serialisation d'une iptype, renvoie son evaluation str""" + """ Serializer for an IpType object field """ + def to_representation(self, value): return value.type + def to_internal_value(self, data): + pass + class IpListSerializer(serializers.ModelSerializer): - """Serialisation d'une iplist, ip_type etant une foreign_key, - on evalue sa methode str""" + """ Serializer for an Ipv4List obejct using the IpType serialization """ + ip_type = IpTypeField(read_only=True) class Meta: @@ -81,16 +90,19 @@ class IpListSerializer(serializers.ModelSerializer): class Ipv6ListSerializer(serializers.ModelSerializer): + """ Serializer for an Ipv6List object """ + class Meta: model = Ipv6List fields = ('ipv6', 'slaac_ip') class InterfaceSerializer(serializers.ModelSerializer): - """Serialisation d'une interface, ipv4, domain et extension sont - des foreign_key, on les override et on les evalue avec des fonctions - get_...""" + """ Serializer for an Interface object. Use SerializerMethodField + to get ForeignKey values """ + ipv4 = IpListSerializer(read_only=True) + # TODO : use serializer.RelatedField to avoid duplicate code mac_address = serializers.SerializerMethodField('get_macaddress') domain = serializers.SerializerMethodField('get_dns') extension = serializers.SerializerMethodField('get_interface_extension') @@ -99,20 +111,29 @@ class InterfaceSerializer(serializers.ModelSerializer): model = Interface fields = ('ipv4', 'mac_address', 'domain', 'extension') - def get_dns(self, obj): + @staticmethod + def get_dns(obj): + """ The name of the associated DNS object """ return obj.domain.name - def get_interface_extension(self, obj): + @staticmethod + def get_interface_extension(obj): + """ The name of the associated Interface object """ return obj.domain.extension.name - def get_macaddress(self, obj): + @staticmethod + def get_macaddress(obj): + """ The string representation of the associated MAC address """ return str(obj.mac_address) class FullInterfaceSerializer(serializers.ModelSerializer): - """Serialisation complete d'une interface avec les ipv6 en plus""" + """ Serializer for an Interface obejct. Use SerializerMethodField + to get ForeignKey values """ + ipv4 = IpListSerializer(read_only=True) ipv6 = Ipv6ListSerializer(read_only=True, many=True) + # TODO : use serializer.RelatedField to avoid duplicate code mac_address = serializers.SerializerMethodField('get_macaddress') domain = serializers.SerializerMethodField('get_dns') extension = serializers.SerializerMethodField('get_interface_extension') @@ -121,26 +142,36 @@ class FullInterfaceSerializer(serializers.ModelSerializer): model = Interface fields = ('ipv4', 'ipv6', 'mac_address', 'domain', 'extension') - def get_dns(self, obj): + @staticmethod + def get_dns(obj): + """ The name of the associated DNS object """ return obj.domain.name - def get_interface_extension(self, obj): + @staticmethod + def get_interface_extension(obj): + """ The name of the associated Extension object """ return obj.domain.extension.name - def get_macaddress(self, obj): + @staticmethod + def get_macaddress(obj): + """ The string representation of the associated MAC address """ return str(obj.mac_address) class ExtensionNameField(serializers.RelatedField): - """Evaluation str d'un objet extension (.example.org)""" + """ Serializer for Extension object field """ + def to_representation(self, value): return value.name + def to_internal_value(self, data): + pass + class TypeSerializer(serializers.ModelSerializer): - """Serialisation d'un iptype : extension et la liste des - ouvertures de port son evalués en get_... etant des - foreign_key ou des relations manytomany""" + """ Serializer for an IpType object. Use SerializerMethodField to + get ForeignKey values """ + extension = ExtensionNameField(read_only=True) ouverture_ports_tcp_in = serializers\ .SerializerMethodField('get_port_policy_input_tcp') @@ -158,7 +189,10 @@ class TypeSerializer(serializers.ModelSerializer): 'ouverture_ports_tcp_in', 'ouverture_ports_tcp_out', 'ouverture_ports_udp_in', 'ouverture_ports_udp_out',) - def get_port_policy(self, obj, protocole, io): + @staticmethod + def get_port_policy(obj, protocole, io): + """ Generic utility function to get the policy for a given + port, protocole and IN or OUT """ if obj.ouverture_ports is None: return [] return map( @@ -196,13 +230,19 @@ class ExtensionSerializer(serializers.ModelSerializer): model = Extension fields = ('name', 'origin', 'origin_v6', 'zone_entry', 'soa') - def get_origin_ip(self, obj): + @staticmethod + def get_origin_ip(obj): + """ The IP of the associated origin for the zone """ return obj.origin.ipv4 - def get_zone_name(self, obj): + @staticmethod + def get_zone_name(obj): + """ The name of the associated zone """ return str(obj.dns_entry) - def get_soa_data(self, obj): + @staticmethod + def get_soa_data(obj): + """ The representation of the associated SOA """ return {'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param} @@ -217,13 +257,19 @@ class MxSerializer(serializers.ModelSerializer): model = Mx fields = ('zone', 'priority', 'name', 'mx_entry') - def get_entry_name(self, obj): + @staticmethod + def get_entry_name(obj): + """ The name of the DNS MX entry """ return str(obj.name) - def get_zone_name(self, obj): + @staticmethod + def get_zone_name(obj): + """ The name of the associated zone of the MX record """ return obj.zone.name - def get_mx_name(self, obj): + @staticmethod + def get_mx_name(obj): + """ The string representation of the entry to add to the DNS """ return str(obj.dns_entry) @@ -237,10 +283,14 @@ class TxtSerializer(serializers.ModelSerializer): model = Txt fields = ('zone', 'txt_entry', 'field1', 'field2') - def get_zone_name(self, obj): + @staticmethod + def get_zone_name(obj): + """ The name of the associated zone """ return str(obj.zone.name) - def get_txt_name(self, obj): + @staticmethod + def get_txt_name(obj): + """ The string representation of the entry to add to the DNS """ return str(obj.dns_entry) @@ -263,10 +313,14 @@ class SrvSerializer(serializers.ModelSerializer): 'srv_entry' ) - def get_extension_name(self, obj): + @staticmethod + def get_extension_name(obj): + """ The name of the associated extension """ return str(obj.extension.name) - def get_srv_name(self, obj): + @staticmethod + def get_srv_name(obj): + """ The string representation of the entry to add to the DNS """ return str(obj.dns_entry) @@ -281,13 +335,19 @@ class NsSerializer(serializers.ModelSerializer): model = Ns fields = ('zone', 'ns', 'ns_entry') - def get_zone_name(self, obj): + @staticmethod + def get_zone_name(obj): + """ The name of the associated zone """ return obj.zone.name - def get_domain_name(self, obj): + @staticmethod + def get_domain_name(obj): + """ The name of the associated NS target """ return str(obj.ns) - def get_text_name(self, obj): + @staticmethod + def get_text_name(obj): + """ The string representation of the entry to add to the DNS """ return str(obj.dns_entry) @@ -302,13 +362,19 @@ class DomainSerializer(serializers.ModelSerializer): model = Domain fields = ('name', 'extension', 'cname', 'cname_entry') - def get_zone_name(self, obj): + @staticmethod + def get_zone_name(obj): + """ The name of the associated zone """ return obj.extension.name - def get_alias_name(self, obj): + @staticmethod + def get_alias_name(obj): + """ The name of the associated alias """ return str(obj.cname) - def get_cname_name(self, obj): + @staticmethod + def get_cname_name(obj): + """ The name of the associated CNAME target """ return str(obj.dns_entry) @@ -322,13 +388,19 @@ class ServicesSerializer(serializers.ModelSerializer): model = Service_link fields = ('server', 'service', 'need_regen') - def get_server_name(self, obj): + @staticmethod + def get_server_name(obj): + """ The name of the associated server """ return str(obj.server.domain.name) - def get_service_name(self, obj): + @staticmethod + def get_service_name(obj): + """ The name of the service name """ return str(obj.service) - def get_regen_status(self, obj): + @staticmethod + def get_regen_status(obj): + """ The string representation of the regen status """ return obj.need_regen() @@ -337,7 +409,19 @@ class OuverturePortsSerializer(serializers.Serializer): ipv4 = serializers.SerializerMethodField() ipv6 = serializers.SerializerMethodField() + def create(self, validated_data): + """ Creates a new object based on the un-serialized data. + Used to implement an abstract inherited method """ + pass + + def update(self, instance, validated_data): + """ Updates an object based on the un-serialized data. + Used to implement an abstract inherited method """ + pass + + @staticmethod def get_ipv4(): + """ The representation of the policy for the IPv4 addresses """ return { i.ipv4.ipv4: { "tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()], @@ -348,7 +432,9 @@ class OuverturePortsSerializer(serializers.Serializer): for i in Interface.objects.all() if i.ipv4 } + @staticmethod def get_ipv6(): + """ The representation of the policy for the IPv6 addresses """ return { i.ipv6: { "tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()], diff --git a/api/tests.py b/api/tests.py index 21fa6d24..bfcda28f 100644 --- a/api/tests.py +++ b/api/tests.py @@ -19,7 +19,10 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""api.tests +The tests for the API module. +""" -from django.test import TestCase +# from django.test import TestCase # Create your tests here. diff --git a/api/utils.py b/api/utils.py index 79aba0a1..d65cff44 100644 --- a/api/utils.py +++ b/api/utils.py @@ -106,7 +106,11 @@ def accept_method(methods): """ def decorator(view): + """The decorator to use on a specific view + """ def wrapper(request, *args, **kwargs): + """The wrapper used for a specific request + """ if request.method in methods: return view(request, *args, **kwargs) else: diff --git a/api/views.py b/api/views.py index 47a34baf..eda4dd59 100644 --- a/api/views.py +++ b/api/views.py @@ -27,18 +27,42 @@ HTML pages such as the login and index pages for a better integration. from django.contrib.auth.decorators import login_required, permission_required from django.views.decorators.csrf import csrf_exempt -from re2o.utils import all_has_access, all_active_assigned_interfaces - +from re2o.utils import ( + all_has_access, + all_active_assigned_interfaces, + filter_active_interfaces +) from users.models import Club from machines.models import ( Service_link, Service, Interface, Domain, - OuverturePortList + IpType, + Mx, + Ns, + Txt, + Srv, + Extension, + OuverturePortList, + OuverturePort ) -from .serializers import * +from .serializers import ( + ServicesSerializer, + ServiceLinkSerializer, + FullInterfaceSerializer, + DomainSerializer, + TypeSerializer, + MxSerializer, + NsSerializer, + TxtSerializer, + SrvSerializer, + ExtensionSerializer, + InterfaceSerializer, + MailingMemberSerializer, + MailingSerializer +) from .utils import JSONError, JSONSuccess, accept_method @@ -46,7 +70,7 @@ from .utils import JSONError, JSONSuccess, accept_method @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def services(request): +def services(_request): """The list of the different services and servers couples Return: @@ -104,7 +128,7 @@ def services_server_service_regen(request, server_name, service_name): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def services_server(request, server_name): +def services_server(_request, server_name): """The list of services attached to a specific server Returns: @@ -122,8 +146,8 @@ def services_server(request, server_name): if not query: return JSONError("This service is not active for this server") - services = query.all() - seria = ServiceLinkSerializer(services, many=True) + services_objects = query.all() + seria = ServiceLinkSerializer(services_objects, many=True) return JSONSuccess(seria.data) @@ -131,7 +155,7 @@ def services_server(request, server_name): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def dns_mac_ip_dns(request): +def dns_mac_ip_dns(_request): """The list of all active interfaces with all the associated infos (MAC, IP, IpType, DNS name and associated zone extension) @@ -160,7 +184,7 @@ def dns_mac_ip_dns(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def dns_alias(request): +def dns_alias(_request): """The list of all the alias used and the DNS info associated Returns: @@ -193,7 +217,7 @@ def dns_alias(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def accesspoint_ip_dns(request): +def accesspoint_ip_dns(_request): """The list of all active interfaces with all the associated infos (MAC, IP, IpType, DNS name and associated zone extension) @@ -225,7 +249,7 @@ def accesspoint_ip_dns(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def dns_corresp(request): +def dns_corresp(_request): """The list of the IpTypes possible with the infos about each Returns: @@ -253,7 +277,7 @@ def dns_corresp(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def dns_mx(request): +def dns_mx(_request): """The list of MX record to add to the DNS Returns: @@ -278,7 +302,7 @@ def dns_mx(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def dns_ns(request): +def dns_ns(_request): """The list of NS record to add to the DNS Returns: @@ -307,7 +331,7 @@ def dns_ns(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def dns_txt(request): +def dns_txt(_request): """The list of TXT record to add to the DNS Returns: @@ -330,7 +354,7 @@ def dns_txt(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def dns_srv(request): +def dns_srv(_request): """The list of SRV record to add to the DNS Returns: @@ -360,7 +384,7 @@ def dns_srv(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def dns_zones(request): +def dns_zones(_request): """The list of the zones managed Returns: @@ -389,7 +413,7 @@ def dns_zones(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def firewall_ouverture_ports(request): +def firewall_ouverture_ports(_request): """The list of the ports authorized to be openned by the firewall Returns: @@ -480,7 +504,7 @@ def firewall_ouverture_ports(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def dhcp_mac_ip(request): +def dhcp_mac_ip(_request): """The list of all active interfaces with all the associated infos (MAC, IP, IpType, DNS name and associated zone extension) @@ -506,7 +530,7 @@ def dhcp_mac_ip(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def mailing_standard(request): +def mailing_standard(_request): """All the available standard mailings. Returns: @@ -525,7 +549,7 @@ def mailing_standard(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def mailing_standard_ml_members(request): +def mailing_standard_ml_members(_request, ml_name): """All the members of a specific standard mailing Returns: @@ -552,7 +576,7 @@ def mailing_standard_ml_members(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def mailing_club(request): +def mailing_club(_request): """All the available club mailings. Returns: @@ -571,7 +595,7 @@ def mailing_club(request): @login_required @permission_required('machines.serveur') @accept_method(['GET']) -def mailing_club_ml_members(request): +def mailing_club_ml_members(_request, ml_name): """All the members of a specific club mailing Returns: From f2c91199d12288ae31372e338e8307af7de08bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 13:39:51 +0000 Subject: [PATCH 11/22] Pylint compliance on cotisations --- cotisations/__init__.py | 3 + cotisations/admin.py | 3 + cotisations/forms.py | 15 +++- cotisations/models.py | 58 ++++++------ cotisations/payment.py | 5 +- cotisations/payment_utils/comnpay.py | 47 +++++----- cotisations/tests.py | 5 +- cotisations/tex.py | 25 +++--- cotisations/urls.py | 128 ++++++++++++++++----------- cotisations/views.py | 28 +++--- 10 files changed, 185 insertions(+), 132 deletions(-) diff --git a/cotisations/__init__.py b/cotisations/__init__.py index df6e4256..ee5a305d 100644 --- a/cotisations/__init__.py +++ b/cotisations/__init__.py @@ -20,5 +20,8 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""cotisations +The app in charge of all the members's cotisations +""" from .acl import * diff --git a/cotisations/admin.py b/cotisations/admin.py index 8186e4e3..587bc066 100644 --- a/cotisations/admin.py +++ b/cotisations/admin.py @@ -20,6 +20,9 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""cotisations.admin +The objects, fields and datastructures visible in the Django admin view +""" from __future__ import unicode_literals diff --git a/cotisations/forms.py b/cotisations/forms.py index 4414de3e..a6647a45 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -38,16 +38,14 @@ from __future__ import unicode_literals from django import forms from django.db.models import Q from django.forms import ModelForm, Form -from django.core.validators import MinValueValidator, MaxValueValidator +from django.core.validators import MinValueValidator from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy as _l -from .models import Article, Paiement, Facture, Banque from preferences.models import OptionalUser -from users.models import User - from re2o.field_permissions import FieldPermissionFormMixin from re2o.mixins import FormRevMixin +from .models import Article, Paiement, Facture, Banque class NewFactureForm(FormRevMixin, ModelForm): @@ -313,6 +311,11 @@ class NewFactureSoldeForm(NewFactureForm): """ def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(NewFactureSoldeForm, self).__init__( + *args, + prefix=prefix, + **kwargs + ) self.fields['cheque'].required = False self.fields['banque'].required = False self.fields['cheque'].label = _('Cheque number') @@ -367,6 +370,10 @@ class RechargeForm(FormRevMixin, Form): super(RechargeForm, self).__init__(*args, **kwargs) def clean_value(self): + """ + Returns a cleaned vlaue from the received form by validating + the value is well inside the possible limits + """ value = self.cleaned_data['value'] if value < OptionalUser.get_cached_value('min_online_payment'): raise forms.ValidationError( diff --git a/cotisations/models.py b/cotisations/models.py index c7b940d6..fa3156f0 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -34,17 +34,16 @@ from __future__ import unicode_literals from dateutil.relativedelta import relativedelta from django.db import models -from django.db.models import Q +from django.db.models import Q, Max from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.forms import ValidationError from django.core.validators import MinValueValidator -from django.db.models import Max from django.utils import timezone -from machines.models import regen from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy as _l +from machines.models import regen from re2o.field_permissions import FieldPermissionModelMixin from re2o.mixins import AclMixin, RevMixin @@ -106,15 +105,15 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): permissions = ( # TODO : change facture to invoice ('change_facture_control', - _l("Can change the \"controlled\" state")), + _l("Can change the \"controlled\" state")), # TODO : seems more likely to be call create_facture_pdf # or create_invoice_pdf ('change_facture_pdf', - _l("Can create a custom PDF invoice")), + _l("Can create a custom PDF invoice")), ('view_facture', - _l("Can see an invoice's details")), + _l("Can see an invoice's details")), ('change_all_facture', - _l("Can edit all the previous invoices")), + _l("Can edit all the previous invoices")), ) verbose_name = _l("Invoice") verbose_name_plural = _l("Invoices") @@ -187,7 +186,7 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): else: return True, None - def can_view(self, user_request, *args, **kwargs): + def can_view(self, user_request, *_args, **_kwargs): if not user_request.has_perm('cotisations.view_facture') and \ self.user != user_request: return False, _("You don't have the right to see someone else's " @@ -198,14 +197,17 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): return True, None @staticmethod - def can_change_control(user_request, *args, **kwargs): + def can_change_control(user_request, *_args, **_kwargs): + """ Returns True if the user can change the 'controlled' status of + this invoice """ return ( user_request.has_perm('cotisations.change_facture_control'), _("You don't have the right to edit the \"controlled\" state.") ) @staticmethod - def can_change_pdf(user_request, *args, **kwargs): + def can_change_pdf(user_request, *_args, **_kwargs): + """ Returns True if the user can change this invoice """ return ( user_request.has_perm('cotisations.change_facture_pdf'), _("You don't have the right to edit an invoice.") @@ -222,7 +224,7 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): @receiver(post_save, sender=Facture) -def facture_post_save(sender, **kwargs): +def facture_post_save(_sender, **kwargs): """ Synchronise the LDAP user after an invoice has been saved. """ @@ -232,7 +234,7 @@ def facture_post_save(sender, **kwargs): @receiver(post_delete, sender=Facture) -def facture_post_delete(sender, **kwargs): +def facture_post_delete(_sender, **kwargs): """ Synchronise the LDAP user after an invoice has been deleted. """ @@ -375,13 +377,14 @@ class Vente(RevMixin, AclMixin, models.Model): def can_edit(self, user_request, *args, **kwargs): if not user_request.has_perm('cotisations.change_vente'): return False, _("You don't have the right to edit the purchases.") - elif not user_request.has_perm('cotisations.change_all_facture') and \ - not self.facture.user.can_edit( - user_request, *args, **kwargs)[0]: + elif (not user_request.has_perm('cotisations.change_all_facture') and + not self.facture.user.can_edit( + user_request, *args, **kwargs + )[0]): return False, _("You don't have the right to edit this user's " "purchases.") - elif not user_request.has_perm('cotisations.change_all_vente') and \ - (self.facture.control or not self.facture.valid): + elif (not user_request.has_perm('cotisations.change_all_vente') and + (self.facture.control or not self.facture.valid)): return False, _("You don't have the right to edit a purchase " "already controlled or invalidated.") else: @@ -399,9 +402,9 @@ class Vente(RevMixin, AclMixin, models.Model): else: return True, None - def can_view(self, user_request, *args, **kwargs): - if not user_request.has_perm('cotisations.view_vente') and \ - self.facture.user != user_request: + def can_view(self, user_request, *_args, **_kwargs): + if (not user_request.has_perm('cotisations.view_vente') and + self.facture.user != user_request): return False, _("You don't have the right to see someone " "else's purchase history.") else: @@ -413,7 +416,7 @@ class Vente(RevMixin, AclMixin, models.Model): # TODO : change vente to purchase @receiver(post_save, sender=Vente) -def vente_post_save(sender, **kwargs): +def vente_post_save(_sender, **kwargs): """ Creates a 'cotisation' related object if needed and synchronise the LDAP user when a purchase has been saved. @@ -431,7 +434,7 @@ def vente_post_save(sender, **kwargs): # TODO : change vente to purchase @receiver(post_delete, sender=Vente) -def vente_post_delete(sender, **kwargs): +def vente_post_delete(_sender, **kwargs): """ Synchronise the LDAP user after a purchase has been deleted. """ @@ -646,7 +649,7 @@ class Cotisation(RevMixin, AclMixin, models.Model): ('change_all_cotisation', _l("Can edit the previous cotisations")), ) - def can_edit(self, user_request, *args, **kwargs): + def can_edit(self, user_request, *_args, **_kwargs): if not user_request.has_perm('cotisations.change_cotisation'): return False, _("You don't have the right to edit a cotisation.") elif not user_request.has_perm('cotisations.change_all_cotisation') \ @@ -657,7 +660,7 @@ class Cotisation(RevMixin, AclMixin, models.Model): else: return True, None - def can_delete(self, user_request, *args, **kwargs): + def can_delete(self, user_request, *_args, **_kwargs): if not user_request.has_perm('cotisations.delete_cotisation'): return False, _("You don't have the right to delete a " "cotisation.") @@ -667,7 +670,7 @@ class Cotisation(RevMixin, AclMixin, models.Model): else: return True, None - def can_view(self, user_request, *args, **kwargs): + def can_view(self, user_request, *_args, **_kwargs): if not user_request.has_perm('cotisations.view_cotisation') and\ self.vente.facture.user != user_request: return False, _("You don't have the right to see someone else's " @@ -680,7 +683,7 @@ class Cotisation(RevMixin, AclMixin, models.Model): @receiver(post_save, sender=Cotisation) -def cotisation_post_save(sender, **kwargs): +def cotisation_post_save(_sender, **_kwargs): """ Mark some services as needing a regeneration after the edition of a cotisation. Indeed the membership status may have changed. @@ -692,11 +695,10 @@ def cotisation_post_save(sender, **kwargs): @receiver(post_delete, sender=Cotisation) -def cotisation_post_delete(sender, **kwargs): +def cotisation_post_delete(_sender, **_kwargs): """ Mark some services as needing a regeneration after the deletion of a cotisation. Indeed the membership status may have changed. """ - cotisation = kwargs['instance'] regen('mac_ip_list') regen('mailing') diff --git a/cotisations/payment.py b/cotisations/payment.py index 8efdd344..b03e1c55 100644 --- a/cotisations/payment.py +++ b/cotisations/payment.py @@ -2,6 +2,9 @@ Here are defined some views dedicated to online payement. """ + +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 @@ -11,8 +14,6 @@ from django.utils.datastructures import MultiValueDictKeyError from django.utils.translation import ugettext as _ from django.http import HttpResponse, HttpResponseBadRequest -from collections import OrderedDict - from preferences.models import AssoOption from .models import Facture from .payment_utils.comnpay import Payment as ComnpayPayment diff --git a/cotisations/payment_utils/comnpay.py b/cotisations/payment_utils/comnpay.py index ad0d8c77..6c73463f 100644 --- a/cotisations/payment_utils/comnpay.py +++ b/cotisations/payment_utils/comnpay.py @@ -1,20 +1,19 @@ +"""cotisations.payment_utils.comnpay +The module in charge of handling the negociation with Comnpay +for online payment +""" + import time from random import randrange import base64 import hashlib from collections import OrderedDict -from itertools import chain class Payment(): - - vad_number = "" - secret_key = "" - urlRetourOK = "" - urlRetourNOK = "" - urlIPN = "" - source = "" - typeTr = "D" + """ The class representing a transaction with all the functions + used during the negociation + """ def __init__(self, vad_number="", secret_key="", urlRetourOK="", urlRetourNOK="", urlIPN="", source="", typeTr="D"): @@ -25,9 +24,13 @@ class Payment(): self.urlIPN = urlIPN self.source = source self.typeTr = typeTr + self.idTransaction = "" def buildSecretHTML(self, produit="Produit", montant="0.00", idTransaction=""): + """ Build an HTML hidden form with the different parameters for the + transaction + """ if idTransaction == "": self.idTransaction = str(time.time()) self.idTransaction += self.vad_number @@ -36,16 +39,16 @@ class Payment(): self.idTransaction = idTransaction array_tpe = OrderedDict( - montant=str(montant), - idTPE=self.vad_number, - idTransaction=self.idTransaction, - devise="EUR", - lang='fr', - nom_produit=produit, - source=self.source, - urlRetourOK=self.urlRetourOK, - urlRetourNOK=self.urlRetourNOK, - typeTr=str(self.typeTr) + montant=str(montant), + idTPE=self.vad_number, + idTransaction=self.idTransaction, + devise="EUR", + lang='fr', + nom_produit=produit, + source=self.source, + urlRetourOK=self.urlRetourOK, + urlRetourNOK=self.urlRetourNOK, + typeTr=str(self.typeTr) ) if self.urlIPN != "": @@ -63,12 +66,14 @@ class Payment(): for key in array_tpe: ret += ''.format( k=key, - v=array_type[key] + v=array_tpe[key] ) return ret - def validSec(self, values, secret_key): + @staticmethod + def validSec(values, secret_key): + """ Check if the secret value is correct """ if "sec" in values: sec = values['sec'] del values["sec"] diff --git a/cotisations/tests.py b/cotisations/tests.py index 21fa6d24..46b9d708 100644 --- a/cotisations/tests.py +++ b/cotisations/tests.py @@ -19,7 +19,10 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""cotisations.tests +The tests for the Cotisations module. +""" -from django.test import TestCase +# from django.test import TestCase # Create your tests here. diff --git a/cotisations/tex.py b/cotisations/tex.py index 95f12b93..f456fe8a 100644 --- a/cotisations/tex.py +++ b/cotisations/tex.py @@ -24,43 +24,44 @@ Module in charge of rendering some LaTex templates. Used to generated PDF invoice. """ +import tempfile +from subprocess import Popen, PIPE +import os +from datetime import datetime + from django.template.loader import get_template from django.template import Context from django.http import HttpResponse from django.conf import settings from django.utils.text import slugify -import tempfile -from subprocess import Popen, PIPE -import os - TEMP_PREFIX = getattr(settings, 'TEX_TEMP_PREFIX', 'render_tex-') CACHE_PREFIX = getattr(settings, 'TEX_CACHE_PREFIX', 'render-tex') CACHE_TIMEOUT = getattr(settings, 'TEX_CACHE_TIMEOUT', 86400) # 1 day -def render_invoice(request, ctx={}): +def render_invoice(_request, ctx={}): """ Render an invoice using some available information such as the current date, the user, the articles, the prices, ... """ filename = '_'.join([ 'invoice', - slugify(ctx['asso_name']), - slugify(ctx['recipient_name']), - str(ctx['DATE'].year), - str(ctx['DATE'].month), - str(ctx['DATE'].day), + slugify(ctx.get('asso_name', "")), + slugify(ctx.get('recipient_name', "")), + str(ctx.get('DATE', datetime.now()).year), + str(ctx.get('DATE', datetime.now()).month), + str(ctx.get('DATE', datetime.now()).day), ]) - r = render_tex(request, 'cotisations/factures.tex', ctx) + r = render_tex(_request, 'cotisations/factures.tex', ctx) r['Content-Disposition'] = 'attachment; filename="{name}.pdf"'.format( name=filename ) return r -def render_tex(request, template, ctx={}): +def render_tex(_request, template, ctx={}): """ Creates a PDF from a LaTex templates using pdflatex. Writes it in a temporary directory and send back an HTTP response for diff --git a/cotisations/urls.py b/cotisations/urls.py index 05b1a7a3..6eafe721 100644 --- a/cotisations/urls.py +++ b/cotisations/urls.py @@ -19,6 +19,9 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""cotisations.urls +The defined URLs for the Cotisations app +""" from __future__ import unicode_literals @@ -29,106 +32,131 @@ from . import views from . import payment urlpatterns = [ - url(r'^new_facture/(?P[0-9]+)$', + url( + r'^new_facture/(?P[0-9]+)$', views.new_facture, name='new-facture' - ), - url(r'^edit_facture/(?P[0-9]+)$', + ), + url( + r'^edit_facture/(?P[0-9]+)$', views.edit_facture, name='edit-facture' - ), - url(r'^del_facture/(?P[0-9]+)$', + ), + url( + r'^del_facture/(?P[0-9]+)$', views.del_facture, name='del-facture' - ), - url(r'^facture_pdf/(?P[0-9]+)$', + ), + url( + r'^facture_pdf/(?P[0-9]+)$', views.facture_pdf, name='facture-pdf' - ), - url(r'^new_facture_pdf/$', + ), + url( + r'^new_facture_pdf/$', views.new_facture_pdf, name='new-facture-pdf' - ), - url(r'^credit_solde/(?P[0-9]+)$', + ), + url( + r'^credit_solde/(?P[0-9]+)$', views.credit_solde, name='credit-solde' - ), - url(r'^add_article/$', + ), + url( + r'^add_article/$', views.add_article, name='add-article' - ), - url(r'^edit_article/(?P[0-9]+)$', + ), + url( + r'^edit_article/(?P[0-9]+)$', views.edit_article, name='edit-article' - ), - url(r'^del_article/$', + ), + url( + r'^del_article/$', views.del_article, name='del-article' - ), - url(r'^add_paiement/$', + ), + url( + r'^add_paiement/$', views.add_paiement, name='add-paiement' - ), - url(r'^edit_paiement/(?P[0-9]+)$', + ), + url( + r'^edit_paiement/(?P[0-9]+)$', views.edit_paiement, name='edit-paiement' - ), - url(r'^del_paiement/$', + ), + url( + r'^del_paiement/$', views.del_paiement, name='del-paiement' - ), - url(r'^add_banque/$', + ), + url( + r'^add_banque/$', views.add_banque, name='add-banque' - ), - url(r'^edit_banque/(?P[0-9]+)$', + ), + url( + r'^edit_banque/(?P[0-9]+)$', views.edit_banque, name='edit-banque' - ), - url(r'^del_banque/$', + ), + url( + r'^del_banque/$', views.del_banque, name='del-banque' - ), - url(r'^index_article/$', + ), + url( + r'^index_article/$', views.index_article, name='index-article' - ), - url(r'^index_banque/$', + ), + url( + r'^index_banque/$', views.index_banque, name='index-banque' - ), - url(r'^index_paiement/$', + ), + url( + r'^index_paiement/$', views.index_paiement, name='index-paiement' - ), - url(r'history/(?P\w+)/(?P[0-9]+)$', + ), + url( + r'history/(?P\w+)/(?P[0-9]+)$', re2o.views.history, name='history', kwargs={'application': 'cotisations'}, - ), - url(r'^control/$', + ), + url( + r'^control/$', views.control, name='control' - ), - url(r'^new_facture_solde/(?P[0-9]+)$', + ), + url( + r'^new_facture_solde/(?P[0-9]+)$', views.new_facture_solde, name='new_facture_solde' - ), - url(r'^recharge/$', + ), + url( + r'^recharge/$', views.recharge, name='recharge' - ), - url(r'^payment/accept/(?P[0-9]+)$', + ), + url( + r'^payment/accept/(?P[0-9]+)$', payment.accept_payment, name='accept_payment' - ), - url(r'^payment/refuse/$', + ), + url( + r'^payment/refuse/$', payment.refuse_payment, name='refuse_payment' - ), - url(r'^payment/ipn/$', + ), + url( + r'^payment/ipn/$', payment.ipn, name='ipn' - ), + ), url(r'^$', views.index, name='index'), ] diff --git a/cotisations/views.py b/cotisations/views.py index c8c68c19..acf8c404 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -23,22 +23,23 @@ # App de gestion des users pour re2o # Goulven Kermarec, Gabriel Détraz # Gplv2 +"""cotisations.views +The different views used in the Cotisations module +""" + from __future__ import unicode_literals import os from django.urls import reverse from django.shortcuts import render, redirect -from django.core.validators import MaxValueValidator -from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.decorators import login_required from django.contrib import messages from django.db.models import ProtectedError -from django.db import transaction from django.db.models import Q from django.forms import modelformset_factory, formset_factory from django.utils import timezone from django.utils.translation import ugettext as _ -from django.views.decorators.csrf import csrf_exempt -from django.views.decorators.debug import sensitive_variables + # Import des models, forms et fonctions re2o from reversion import revisions as reversion from users.models import User @@ -70,7 +71,6 @@ from .forms import ( SelectUserArticleForm, SelectClubArticleForm, CreditSoldeForm, - NewFactureSoldeForm, RechargeForm ) from . import payment as online_payment @@ -122,7 +122,7 @@ def new_facture(request, user, userid): if user_balance: # TODO : change Paiement to Payment if new_invoice_instance.paiement == ( - Paiement.objects.get_or_create(moyen='solde')[0] + Paiement.objects.get_or_create(moyen='solde')[0] ): total_price = 0 for art_item in articles: @@ -263,7 +263,7 @@ def new_facture_pdf(request): # TODO : change facture to invoice @login_required @can_view(Facture) -def facture_pdf(request, facture, factureid): +def facture_pdf(request, facture, _factureid): """ View used to generate a PDF file from an existing invoice in database Creates a line for each Purchase (thus article sold) and generate the @@ -306,7 +306,7 @@ def facture_pdf(request, facture, factureid): # TODO : change facture to invoice @login_required @can_edit(Facture) -def edit_facture(request, facture, factureid): +def edit_facture(request, facture, _factureid): """ View used to edit an existing invoice. Articles can be added or remove to the invoice and quantity @@ -347,7 +347,7 @@ def edit_facture(request, facture, factureid): # TODO : change facture to invoice @login_required @can_delete(Facture) -def del_facture(request, facture, factureid): +def del_facture(request, facture, _factureid): """ View used to delete an existing invocie. """ @@ -368,7 +368,7 @@ def del_facture(request, facture, factureid): @login_required @can_create(Facture) @can_edit(User) -def credit_solde(request, user, userid): +def credit_solde(request, user, _userid): """ View used to edit the balance of a user. Can be use either to increase or decrease a user's balance. @@ -425,7 +425,7 @@ def add_article(request): @login_required @can_edit(Article) -def edit_article(request, article_instance, articleid): +def edit_article(request, article_instance, _articleid): """ View used to edit an article. """ @@ -489,7 +489,7 @@ def add_paiement(request): # TODO : chnage paiement to Payment @login_required @can_edit(Paiement) -def edit_paiement(request, paiement_instance, paiementid): +def edit_paiement(request, paiement_instance, _paiementid): """ View used to edit a payment method. """ @@ -567,7 +567,7 @@ def add_banque(request): # TODO : change banque to bank @login_required @can_edit(Banque) -def edit_banque(request, banque_instance, banqueid): +def edit_banque(request, banque_instance, _bbanqueid): """ View used to edit a bank. """ From 2114a6ebbaf7d403e1207c089483f999d8b3f1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 15:30:14 +0000 Subject: [PATCH 12/22] Pylint compliance on logs --- freeradius_utils/auth.py | 30 ++++++++++++++++-------------- logs/__init__.py | 3 +++ logs/admin.py | 28 ---------------------------- logs/models.py | 28 ---------------------------- logs/templatetags/logs_extra.py | 4 ++++ logs/tests.py | 5 ++++- logs/views.py | 24 +----------------------- 7 files changed, 28 insertions(+), 94 deletions(-) delete mode 100644 logs/admin.py delete mode 100644 logs/models.py diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index b25fa003..aafa0a50 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -35,20 +35,16 @@ https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_pyth Inspiré du travail de Daniel Stan au Crans """ -import logging -import netaddr -import radiusd # Module magique freeradius (radiusd.py is dummy) -import binascii -import hashlib import os import sys +import logging +import radiusd # Module magique freeradius (radiusd.py is dummy) + from django.core.wsgi import get_wsgi_application - -import argparse - from django.db.models import Q + from machines.models import Interface, IpList, Nas, Domain -from topologie.models import Room, Port, Switch +from topologie.models import Port, Switch from users.models import User from preferences.models import OptionalTopologie @@ -109,7 +105,8 @@ def radius_event(fun): tuples en entrée en un dictionnaire.""" def new_f(auth_data): - if type(auth_data) == dict: + """ The function transforming the tuples as dict """ + if isinstance(auth_data, dict): data = auth_data else: data = dict() @@ -188,6 +185,9 @@ def authorize(data): @radius_event def post_auth(data): + """ Function called after the user is authenticated + """ + nas = data.get('NAS-IP-Address', data.get('NAS-Identifier', None)) nas_instance = find_nas_from_request(nas) # Toutes les reuquètes non proxifiées @@ -220,7 +220,7 @@ def post_auth(data): .filter(stack_member_id=id_stack_member) .prefetch_related( 'interface_set__domain__extension' - ) + ) .first()) # On récupère le numéro du port sur l'output de freeradius. # La ligne suivante fonctionne pour cisco, HP et Juniper @@ -229,7 +229,7 @@ def post_auth(data): sw_name, room, reason, vlan_id = out log_message = '(fil) %s -> %s [%s%s]' % ( - sw_name + u":" + port + u"/" + unicode(room), + sw_name + u":" + port + u"/" + str(room), mac, vlan_id, (reason and u': ' + reason).encode('utf-8') @@ -251,6 +251,7 @@ def post_auth(data): return radiusd.RLM_MODULE_OK +# TODO : remove this function @radius_event def dummy_fun(_): """Do nothing, successfully. (C'est pour avoir un truc à mettre)""" @@ -259,11 +260,12 @@ def dummy_fun(_): def detach(_=None): """Appelé lors du déchargement du module (enfin, normalement)""" - print "*** goodbye from auth.py ***" + print("*** goodbye from auth.py ***") return radiusd.RLM_MODULE_OK def find_nas_from_request(nas_id): + """ Get the nas object from its ID """ nas = (Interface.objects .filter( Q(domain=Domain.objects.filter(name=nas_id)) | @@ -443,7 +445,7 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, return (sw_name, room, u'Erreur dans le register mac %s' % ( - reason + unicode(mac_address) + reason + str(mac_address) ), VLAN_NOK) else: diff --git a/logs/__init__.py b/logs/__init__.py index df6e4256..20338670 100644 --- a/logs/__init__.py +++ b/logs/__init__.py @@ -20,5 +20,8 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""logs +The app in charge of the stats and logs of what's happening in re2o +""" from .acl import * diff --git a/logs/admin.py b/logs/admin.py deleted file mode 100644 index bcdc4f1d..00000000 --- a/logs/admin.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il -# se veut agnostique au réseau considéré, de manière à être installable en -# quelques clics. -# -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec -# Copyright © 2017 Augustin Lemesle -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -from __future__ import unicode_literals - -from django.contrib import admin - -# Register your models here. diff --git a/logs/models.py b/logs/models.py deleted file mode 100644 index 934704ba..00000000 --- a/logs/models.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il -# se veut agnostique au réseau considéré, de manière à être installable en -# quelques clics. -# -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Goulven Kermarec -# Copyright © 2017 Augustin Lemesle -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# 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., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -from __future__ import unicode_literals - -from django.db import models - -# Create your models here. diff --git a/logs/templatetags/logs_extra.py b/logs/templatetags/logs_extra.py index e67d8df2..97f60ec8 100644 --- a/logs/templatetags/logs_extra.py +++ b/logs/templatetags/logs_extra.py @@ -20,6 +20,9 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""logs.templatetags.logs_extra +A templatetag to get the class name for a given object +""" from django import template @@ -28,4 +31,5 @@ register = template.Library() @register.filter def classname(obj): + """ Returns the object class name """ return obj.__class__.__name__ diff --git a/logs/tests.py b/logs/tests.py index 21fa6d24..65679fa3 100644 --- a/logs/tests.py +++ b/logs/tests.py @@ -19,7 +19,10 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""logs.tests +The tests for the Logs module. +""" -from django.test import TestCase +# from django.test import TestCase # Create your tests here. diff --git a/logs/views.py b/logs/views.py index 8763faa6..afb0a118 100644 --- a/logs/views.py +++ b/logs/views.py @@ -46,8 +46,6 @@ from django.db.models import Count, Max from reversion.models import Revision from reversion.models import Version, ContentType -from time import time - from users.models import ( User, ServiceUser, @@ -109,15 +107,6 @@ from re2o.acl import ( from re2o.utils import all_active_assigned_interfaces_count from re2o.utils import all_active_interfaces_count, SortTable -STATS_DICT = { - 0: ["Tout", 36], - 1: ["1 mois", 1], - 2: ["2 mois", 2], - 3: ["6 mois", 6], - 4: ["1 an", 12], - 5: ["2 an", 24], -} - @login_required @can_view_app('logs') @@ -418,12 +407,6 @@ def stats_users(request): nombre de machines par user, d'etablissements par user, de moyens de paiements par user, de banque par user, de bannissement par user, etc""" - onglet = request.GET.get('onglet') - try: - _search_field = STATS_DICT[onglet] - except KeyError: - _search_field = STATS_DICT[0] - onglet = 0 stats = { 'Utilisateur': { 'Machines': User.objects.annotate( @@ -458,11 +441,7 @@ def stats_users(request): ).order_by('-num')[:10], }, } - return render(request, 'logs/stats_users.html', { - 'stats_list': stats, - 'stats_dict': STATS_DICT, - 'active_field': onglet - }) + return render(request, 'logs/stats_users.html', {'stats_list': stats}) @login_required @@ -485,7 +464,6 @@ def stats_actions(request): @can_view_app('users') def stats_droits(request): """Affiche la liste des droits et les users ayant chaque droit""" - depart = time() stats_list = {} for droit in ListRight.objects.all().select_related('group_ptr'): From e88141db569d9b78d735526299f34bab820c0b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 18:19:02 +0000 Subject: [PATCH 13/22] Pylint compliance on machines --- machines/__init__.py | 3 + machines/admin.py | 21 ++++ machines/forms.py | 6 +- machines/models.py | 124 +++++++++++-------- machines/serializers.py | 163 ++++++++++++++++++------- machines/tests.py | 5 +- machines/urls.py | 3 + machines/views.py | 259 ++++++++++++++++++++++++---------------- 8 files changed, 385 insertions(+), 199 deletions(-) diff --git a/machines/__init__.py b/machines/__init__.py index df6e4256..f874399a 100644 --- a/machines/__init__.py +++ b/machines/__init__.py @@ -20,5 +20,8 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""machines +The app in charge of everything related to the machines, the interface, ... +""" from .acl import * diff --git a/machines/admin.py b/machines/admin.py index 76121bd5..0f85007c 100644 --- a/machines/admin.py +++ b/machines/admin.py @@ -20,6 +20,9 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""machines.admin +The objects, fields and datastructures visible in the Django admin view +""" from __future__ import unicode_literals @@ -44,74 +47,92 @@ from .models import ( class MachineAdmin(VersionAdmin): + """ Admin view of a Machine object """ pass class Ipv6ListAdmin(VersionAdmin): + """ Admin view of a Ipv6List object """ pass class IpTypeAdmin(VersionAdmin): + """ Admin view of a IpType object """ pass class MachineTypeAdmin(VersionAdmin): + """ Admin view of a MachineType object """ pass class VlanAdmin(VersionAdmin): + """ Admin view of a Vlan object """ pass class ExtensionAdmin(VersionAdmin): + """ Admin view of a Extension object """ pass class SOAAdmin(VersionAdmin): + """ Admin view of a SOA object """ pass class MxAdmin(VersionAdmin): + """ Admin view of a MX object """ pass class NsAdmin(VersionAdmin): + """ Admin view of a NS object """ pass class TxtAdmin(VersionAdmin): + """ Admin view of a TXT object """ pass class SrvAdmin(VersionAdmin): + """ Admin view of a SRV object """ pass class NasAdmin(VersionAdmin): + """ Admin view of a Nas object """ pass class IpListAdmin(VersionAdmin): + """ Admin view of a Ipv4List object """ pass class OuverturePortAdmin(VersionAdmin): + """ Admin view of a OuverturePort object """ pass class OuverturePortListAdmin(VersionAdmin): + """ Admin view of a OuverturePortList object """ pass class InterfaceAdmin(VersionAdmin): + """ Admin view of a Interface object """ list_display = ('machine', 'type', 'mac_address', 'ipv4', 'details') class DomainAdmin(VersionAdmin): + """ Admin view of a Domain object """ list_display = ('interface_parent', 'name', 'extension', 'cname') class ServiceAdmin(VersionAdmin): + """ Admin view of a ServiceAdmin object """ list_display = ('service_type', 'min_time_regen', 'regular_time_regen') diff --git a/machines/forms.py b/machines/forms.py index 8133f622..a838719f 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -137,10 +137,10 @@ class AliasForm(FormRevMixin, ModelForm): prefix = kwargs.pop('prefix', self.Meta.model.__name__) user = kwargs.pop('user') super(AliasForm, self).__init__(*args, prefix=prefix, **kwargs) - can_use_all, reason = Extension.can_use_all(user) + can_use_all, _reason = Extension.can_use_all(user) if not can_use_all: self.fields['extension'].queryset = Extension.objects.filter( - need_infra=False + need_infra=False ) @@ -480,6 +480,8 @@ class ServiceForm(FormRevMixin, ModelForm): )) def save(self, commit=True): + # TODO : None of the parents of ServiceForm use the commit + # parameter in .save() instance = super(ServiceForm, self).save(commit=False) if commit: instance.save() diff --git a/machines/models.py b/machines/models.py index e3024578..927ee4d0 100644 --- a/machines/models.py +++ b/machines/models.py @@ -20,14 +20,17 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""machines.models +The models definitions for the Machines app +""" from __future__ import unicode_literals from datetime import timedelta import re -from netaddr import mac_bare, EUI, IPSet, IPRange, IPNetwork, IPAddress from ipaddress import IPv6Address from itertools import chain +from netaddr import mac_bare, EUI, IPSet, IPRange, IPNetwork, IPAddress from django.db import models from django.db.models.signals import post_save, post_delete @@ -67,12 +70,13 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): "Peut changer le propriétaire d'une machine"), ) - def get_instance(machineid, *args, **kwargs): + @classmethod + def get_instance(cls, machineid, *_args, **_kwargs): """Get the Machine instance with machineid. :param userid: The id :return: The user """ - return Machine.objects.get(pk=machineid) + return cls.objects.get(pk=machineid) def linked_objects(self): """Return linked objects : machine and domain. @@ -85,7 +89,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): ) @staticmethod - def can_change_user(user_request, *args, **kwargs): + def can_change_user(user_request, *_args, **_kwargs): """Checks if an user is allowed to change the user who owns a Machine. @@ -99,7 +103,8 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): return (user_request.has_perm('machines.change_machine_user'), "Vous ne pouvez pas modifier l'utilisateur de la machine.") - def can_view_all(user_request, *args, **kwargs): + @staticmethod + def can_view_all(user_request, *_args, **_kwargs): """Vérifie qu'on peut bien afficher l'ensemble des machines, droit particulier correspondant :param user_request: instance user qui fait l'edition @@ -109,7 +114,8 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): "machines sans permission") return True, None - def can_create(user_request, userid, *args, **kwargs): + @staticmethod + def can_create(user_request, userid, *_args, **_kwargs): """Vérifie qu'un user qui fait la requète peut bien créer la machine et n'a pas atteint son quota, et crée bien une machine à lui :param user_request: Utilisateur qui fait la requête @@ -172,7 +178,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): "d'un autre user que vous sans droit") return True, None - def can_view(self, user_request, *args, **kwargs): + def can_view(self, user_request, *_args, **_kwargs): """Vérifie qu'on peut bien voir cette instance particulière (soit machine de soi, soit droit particulier :param self: instance machine à éditer @@ -218,7 +224,8 @@ class MachineType(RevMixin, AclMixin, models.Model): machinetype""" return Interface.objects.filter(type=self) - def can_use_all(user_request, *args, **kwargs): + @staticmethod + def can_use_all(user_request, *_args, **_kwargs): """Check if an user can use every MachineType. Args: @@ -328,10 +335,10 @@ class IpType(RevMixin, AclMixin, models.Model): return else: for ipv6 in Ipv6List.objects.filter( - interface__in=Interface.objects.filter( - type__in=MachineType.objects.filter(ip_type=self) - ) - ): + interface__in=Interface.objects.filter( + type__in=MachineType.objects.filter(ip_type=self) + ) + ): ipv6.check_and_replace_prefix(prefix=self.prefix_v6) def clean(self): @@ -360,7 +367,8 @@ class IpType(RevMixin, AclMixin, models.Model): self.clean() super(IpType, self).save(*args, **kwargs) - def can_use_all(user_request, *args, **kwargs): + @staticmethod + def can_use_all(user_request, *_args, **_kwargs): """Superdroit qui permet d'utiliser toutes les extensions sans restrictions :param user_request: instance user qui fait l'edition @@ -555,7 +563,8 @@ class Extension(RevMixin, AclMixin, models.Model): entry += "@ IN AAAA " + str(self.origin_v6) return entry - def can_use_all(user_request, *args, **kwargs): + @staticmethod + def can_use_all(user_request, *_args, **_kwargs): """Superdroit qui permet d'utiliser toutes les extensions sans restrictions :param user_request: instance user qui fait l'edition @@ -644,6 +653,7 @@ class Txt(RevMixin, AclMixin, models.Model): class Srv(RevMixin, AclMixin, models.Model): + """ A SRV record """ PRETTY_NAME = "Enregistrement Srv" TCP = 'TCP' @@ -874,7 +884,8 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): correspondent pas") super(Interface, self).save(*args, **kwargs) - def can_create(user_request, machineid, *args, **kwargs): + @staticmethod + def can_create(user_request, machineid, *_args, **_kwargs): """Verifie que l'user a les bons droits infra pour créer une interface, ou bien que la machine appartient bien à l'user :param macineid: Id de la machine parente de l'interface @@ -903,7 +914,9 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): return True, None @staticmethod - def can_change_machine(user_request, *args, **kwargs): + def can_change_machine(user_request, *_args, **_kwargs): + """Check if a user can change the machine associated with an + Interface object """ return (user_request.has_perm('machines.change_interface_machine'), "Droit requis pour changer la machine") @@ -941,7 +954,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): "d'un autre user que vous sans droit") return True, None - def can_view(self, user_request, *args, **kwargs): + def can_view(self, user_request, *_args, **_kwargs): """Vérifie qu'on peut bien voir cette instance particulière avec droit view objet ou qu'elle appartient à l'user :param self: instance interface à voir @@ -981,6 +994,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): + """ A list of IPv6 """ PRETTY_NAME = 'Enregistrements Ipv6 des machines' ipv6 = models.GenericIPAddressField( @@ -1001,7 +1015,8 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): "Peut changer la valeur slaac sur une ipv6"), ) - def can_create(user_request, interfaceid, *args, **kwargs): + @staticmethod + def can_create(user_request, interfaceid, *_args, **_kwargs): """Verifie que l'user a les bons droits infra pour créer une ipv6, ou possède l'interface associée :param interfaceid: Id de l'interface associée à cet objet domain @@ -1018,7 +1033,8 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): return True, None @staticmethod - def can_change_slaac_ip(user_request, *args, **kwargs): + def can_change_slaac_ip(user_request, *_args, **_kwargs): + """ Check if a user can change the slaac value """ return (user_request.has_perm('machines.change_ipv6list_slaac_ip'), "Droit requis pour changer la valeur slaac ip") @@ -1056,7 +1072,7 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): "d'un autre user que vous sans droit") return True, None - def can_view(self, user_request, *args, **kwargs): + def can_view(self, user_request, *_args, **_kwargs): """Vérifie qu'on peut bien voir cette instance particulière avec droit view objet ou qu'elle appartient à l'user :param self: instance interface à voir @@ -1165,7 +1181,7 @@ class Domain(RevMixin, AclMixin, models.Model): if self.cname == self: raise ValidationError("On ne peut créer un cname sur lui même") HOSTNAME_LABEL_PATTERN = re.compile( - "(?!-)[A-Z\d-]+(?= max_lambdauser_aliases: + cname__in=Domain.objects.filter( + interface_parent__in=(interface.machine.user + .user_interfaces()) + ) + ).count() >= max_lambdauser_aliases: return False, (u"Vous avez atteint le maximum d'alias " "autorisés que vous pouvez créer vous même " "(%s) " % max_lambdauser_aliases) return True, None - def can_edit(self, user_request, *args, **kwargs): + def can_edit(self, user_request, *_args, **_kwargs): """Verifie que l'user a les bons droits pour editer cette instance domain :param self: Instance domain à editer @@ -1248,7 +1265,7 @@ class Domain(RevMixin, AclMixin, models.Model): "d'un autre user que vous sans droit") return True, None - def can_delete(self, user_request, *args, **kwargs): + def can_delete(self, user_request, *_args, **_kwargs): """Verifie que l'user a les bons droits delete object pour del cette instance domain, ou qu'elle lui appartient :param self: Instance domain à del @@ -1260,7 +1277,7 @@ class Domain(RevMixin, AclMixin, models.Model): "machine d'un autre user que vous sans droit") return True, None - def can_view(self, user_request, *args, **kwargs): + def can_view(self, user_request, *_args, **_kwargs): """Vérifie qu'on peut bien voir cette instance particulière avec droit view objet ou qu'elle appartient à l'user :param self: instance domain à voir @@ -1277,6 +1294,7 @@ class Domain(RevMixin, AclMixin, models.Model): class IpList(RevMixin, AclMixin, models.Model): + """ A list of IPv4 """ PRETTY_NAME = "Addresses ipv4" ipv4 = models.GenericIPAddressField(protocol='IPv4', unique=True) @@ -1338,8 +1356,8 @@ class Service(RevMixin, AclMixin, models.Model): """ Django ne peut créer lui meme les relations manytomany avec table intermediaire explicite""" for serv in servers.exclude( - pk__in=Interface.objects.filter(service=self) - ): + pk__in=Interface.objects.filter(service=self) + ): link = Service_link(service=self, server=serv) link.save() Service_link.objects.filter(service=self).exclude(server__in=servers)\ @@ -1407,7 +1425,7 @@ class OuverturePortList(RevMixin, AclMixin, models.Model): ("view_ouvertureportlist", "Peut voir un objet ouvertureport"), ) - def can_delete(self, user_request, *args, **kwargs): + def can_delete(self, user_request, *_args, **_kwargs): """Verifie que l'user a les bons droits bureau pour delete cette instance ouvertureportlist :param self: Instance ouvertureportlist à delete @@ -1501,7 +1519,7 @@ class OuverturePort(RevMixin, AclMixin, models.Model): @receiver(post_save, sender=Machine) -def machine_post_save(sender, **kwargs): +def machine_post_save(_sender, **kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la modification d'une machine""" user = kwargs['instance'].user @@ -1511,7 +1529,7 @@ def machine_post_save(sender, **kwargs): @receiver(post_delete, sender=Machine) -def machine_post_delete(sender, **kwargs): +def machine_post_delete(_sender, **kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la suppression d'une machine""" machine = kwargs['instance'] @@ -1522,7 +1540,7 @@ def machine_post_delete(sender, **kwargs): @receiver(post_save, sender=Interface) -def interface_post_save(sender, **kwargs): +def interface_post_save(_sender, **kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la modification d'une interface""" interface = kwargs['instance'] @@ -1535,7 +1553,7 @@ def interface_post_save(sender, **kwargs): @receiver(post_delete, sender=Interface) -def interface_post_delete(sender, **kwargs): +def interface_post_delete(_sender, **kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la suppression d'une interface""" interface = kwargs['instance'] @@ -1544,7 +1562,7 @@ def interface_post_delete(sender, **kwargs): @receiver(post_save, sender=IpType) -def iptype_post_save(sender, **kwargs): +def iptype_post_save(_sender, **kwargs): """Generation des objets ip après modification d'un range ip""" iptype = kwargs['instance'] iptype.gen_ip_range() @@ -1552,7 +1570,7 @@ def iptype_post_save(sender, **kwargs): @receiver(post_save, sender=MachineType) -def machinetype_post_save(sender, **kwargs): +def machinetype_post_save(_sender, **kwargs): """Mise à jour des interfaces lorsque changement d'attribution d'une machinetype (changement iptype parent)""" machinetype = kwargs['instance'] @@ -1561,84 +1579,84 @@ def machinetype_post_save(sender, **kwargs): @receiver(post_save, sender=Domain) -def domain_post_save(sender, **kwargs): +def domain_post_save(_sender, **_kwargs): """Regeneration dns après modification d'un domain object""" regen('dns') @receiver(post_delete, sender=Domain) -def domain_post_delete(sender, **kwargs): +def domain_post_delete(_sender, **_kwargs): """Regeneration dns après suppression d'un domain object""" regen('dns') @receiver(post_save, sender=Extension) -def extension_post_save(sender, **kwargs): +def extension_post_save(_sender, **_kwargs): """Regeneration dns après modification d'une extension""" regen('dns') @receiver(post_delete, sender=Extension) -def extension_post_selete(sender, **kwargs): +def extension_post_selete(_sender, **_kwargs): """Regeneration dns après suppression d'une extension""" regen('dns') @receiver(post_save, sender=SOA) -def soa_post_save(sender, **kwargs): +def soa_post_save(_sender, **_kwargs): """Regeneration dns après modification d'un SOA""" regen('dns') @receiver(post_delete, sender=SOA) -def soa_post_delete(sender, **kwargs): +def soa_post_delete(_sender, **_kwargs): """Regeneration dns après suppresson d'un SOA""" regen('dns') @receiver(post_save, sender=Mx) -def mx_post_save(sender, **kwargs): +def mx_post_save(_sender, **_kwargs): """Regeneration dns après modification d'un MX""" regen('dns') @receiver(post_delete, sender=Mx) -def mx_post_delete(sender, **kwargs): +def mx_post_delete(_sender, **_kwargs): """Regeneration dns après suppresson d'un MX""" regen('dns') @receiver(post_save, sender=Ns) -def ns_post_save(sender, **kwargs): +def ns_post_save(_sender, **_kwargs): """Regeneration dns après modification d'un NS""" regen('dns') @receiver(post_delete, sender=Ns) -def ns_post_delete(sender, **kwargs): +def ns_post_delete(_sender, **_kwargs): """Regeneration dns après modification d'un NS""" regen('dns') @receiver(post_save, sender=Txt) -def text_post_save(sender, **kwargs): +def text_post_save(_sender, **_kwargs): """Regeneration dns après modification d'un TXT""" regen('dns') @receiver(post_delete, sender=Txt) -def text_post_delete(sender, **kwargs): +def text_post_delete(_sender, **_kwargs): """Regeneration dns après modification d'un TX""" regen('dns') @receiver(post_save, sender=Srv) -def srv_post_save(sender, **kwargs): +def srv_post_save(_sender, **_kwargs): """Regeneration dns après modification d'un SRV""" regen('dns') @receiver(post_delete, sender=Srv) -def srv_post_delete(sender, **kwargs): +def srv_post_delete(_sender, **_kwargs): """Regeneration dns après modification d'un SRV""" regen('dns') diff --git a/machines/serializers.py b/machines/serializers.py index cdd6b705..9476e9d0 100644 --- a/machines/serializers.py +++ b/machines/serializers.py @@ -22,6 +22,10 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Augustin Lemesle +"""machines.serializers +Serializers for the Machines app +""" + from rest_framework import serializers from machines.models import ( @@ -29,28 +33,30 @@ from machines.models import ( IpType, Extension, IpList, - MachineType, Domain, Txt, Mx, Srv, Service_link, Ns, - OuverturePortList, OuverturePort, Ipv6List ) class IpTypeField(serializers.RelatedField): - """Serialisation d'une iptype, renvoie son evaluation str""" + """ Serializer for an IpType object field """ + def to_representation(self, value): return value.type + def to_internal_value(self, data): + pass + class IpListSerializer(serializers.ModelSerializer): - """Serialisation d'une iplist, ip_type etant une foreign_key, - on evalue sa methode str""" + """ Serializer for an Ipv4List obejct using the IpType serialization """ + ip_type = IpTypeField(read_only=True) class Meta: @@ -59,16 +65,19 @@ class IpListSerializer(serializers.ModelSerializer): class Ipv6ListSerializer(serializers.ModelSerializer): + """ Serializer for an Ipv6List object """ + class Meta: model = Ipv6List fields = ('ipv6', 'slaac_ip') class InterfaceSerializer(serializers.ModelSerializer): - """Serialisation d'une interface, ipv4, domain et extension sont - des foreign_key, on les override et on les evalue avec des fonctions - get_...""" + """ Serializer for an Interface object. Use SerializerMethodField + to get ForeignKey values """ + ipv4 = IpListSerializer(read_only=True) + # TODO : use serializer.RelatedField to avoid duplicate code mac_address = serializers.SerializerMethodField('get_macaddress') domain = serializers.SerializerMethodField('get_dns') extension = serializers.SerializerMethodField('get_interface_extension') @@ -77,20 +86,29 @@ class InterfaceSerializer(serializers.ModelSerializer): model = Interface fields = ('ipv4', 'mac_address', 'domain', 'extension') - def get_dns(self, obj): + @staticmethod + def get_dns(obj): + """ The name of the associated DNS object """ return obj.domain.name - def get_interface_extension(self, obj): + @staticmethod + def get_interface_extension(obj): + """ The name of the associated Interface object """ return obj.domain.extension.name - def get_macaddress(self, obj): + @staticmethod + def get_macaddress(obj): + """ The string representation of the associated MAC address """ return str(obj.mac_address) class FullInterfaceSerializer(serializers.ModelSerializer): - """Serialisation complete d'une interface avec les ipv6 en plus""" + """ Serializer for an Interface obejct. Use SerializerMethodField + to get ForeignKey values """ + ipv4 = IpListSerializer(read_only=True) ipv6 = Ipv6ListSerializer(read_only=True, many=True) + # TODO : use serializer.RelatedField to avoid duplicate code mac_address = serializers.SerializerMethodField('get_macaddress') domain = serializers.SerializerMethodField('get_dns') extension = serializers.SerializerMethodField('get_interface_extension') @@ -99,26 +117,36 @@ class FullInterfaceSerializer(serializers.ModelSerializer): model = Interface fields = ('ipv4', 'ipv6', 'mac_address', 'domain', 'extension') - def get_dns(self, obj): + @staticmethod + def get_dns(obj): + """ The name of the associated DNS object """ return obj.domain.name - def get_interface_extension(self, obj): + @staticmethod + def get_interface_extension(obj): + """ The name of the associated Extension object """ return obj.domain.extension.name - def get_macaddress(self, obj): + @staticmethod + def get_macaddress(obj): + """ The string representation of the associated MAC address """ return str(obj.mac_address) class ExtensionNameField(serializers.RelatedField): - """Evaluation str d'un objet extension (.example.org)""" + """ Serializer for Extension object field """ + def to_representation(self, value): return value.name + def to_internal_value(self, data): + pass + class TypeSerializer(serializers.ModelSerializer): - """Serialisation d'un iptype : extension et la liste des - ouvertures de port son evalués en get_... etant des - foreign_key ou des relations manytomany""" + """ Serializer for an IpType object. Use SerializerMethodField to + get ForeignKey values. Infos about the general port policy is added """ + extension = ExtensionNameField(read_only=True) ouverture_ports_tcp_in = serializers\ .SerializerMethodField('get_port_policy_input_tcp') @@ -136,7 +164,10 @@ class TypeSerializer(serializers.ModelSerializer): 'ouverture_ports_tcp_in', 'ouverture_ports_tcp_out', 'ouverture_ports_udp_in', 'ouverture_ports_udp_out',) - def get_port_policy(self, obj, protocole, io): + @staticmethod + def get_port_policy(obj, protocole, io): + """ Generic utility function to get the policy for a given + port, protocole and IN or OUT """ if obj.ouverture_ports is None: return [] return map( @@ -174,13 +205,19 @@ class ExtensionSerializer(serializers.ModelSerializer): model = Extension fields = ('name', 'origin', 'origin_v6', 'zone_entry', 'soa') - def get_origin_ip(self, obj): - return getattr(obj.origin, 'ipv4', None) + @staticmethod + def get_origin_ip(obj): + """ The IP of the associated origin for the zone """ + return obj.origin.ipv4 - def get_zone_name(self, obj): + @staticmethod + def get_zone_name(obj): + """ The name of the associated zone """ return str(obj.dns_entry) - def get_soa_data(self, obj): + @staticmethod + def get_soa_data(obj): + """ The representation of the associated SOA """ return {'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param} @@ -195,13 +232,19 @@ class MxSerializer(serializers.ModelSerializer): model = Mx fields = ('zone', 'priority', 'name', 'mx_entry') - def get_entry_name(self, obj): + @staticmethod + def get_entry_name(obj): + """ The name of the DNS MX entry """ return str(obj.name) - def get_zone_name(self, obj): + @staticmethod + def get_zone_name(obj): + """ The name of the associated zone of the MX record """ return obj.zone.name - def get_mx_name(self, obj): + @staticmethod + def get_mx_name(obj): + """ The string representation of the entry to add to the DNS """ return str(obj.dns_entry) @@ -215,10 +258,14 @@ class TxtSerializer(serializers.ModelSerializer): model = Txt fields = ('zone', 'txt_entry', 'field1', 'field2') - def get_zone_name(self, obj): + @staticmethod + def get_zone_name(obj): + """ The name of the associated zone """ return str(obj.zone.name) - def get_txt_name(self, obj): + @staticmethod + def get_txt_name(obj): + """ The string representation of the entry to add to the DNS """ return str(obj.dns_entry) @@ -241,10 +288,14 @@ class SrvSerializer(serializers.ModelSerializer): 'srv_entry' ) - def get_extension_name(self, obj): + @staticmethod + def get_extension_name(obj): + """ The name of the associated extension """ return str(obj.extension.name) - def get_srv_name(self, obj): + @staticmethod + def get_srv_name(obj): + """ The string representation of the entry to add to the DNS """ return str(obj.dns_entry) @@ -259,13 +310,19 @@ class NsSerializer(serializers.ModelSerializer): model = Ns fields = ('zone', 'ns', 'ns_entry') - def get_zone_name(self, obj): + @staticmethod + def get_zone_name(obj): + """ The name of the associated zone """ return obj.zone.name - def get_domain_name(self, obj): + @staticmethod + def get_domain_name(obj): + """ The name of the associated NS target """ return str(obj.ns) - def get_text_name(self, obj): + @staticmethod + def get_text_name(obj): + """ The string representation of the entry to add to the DNS """ return str(obj.dns_entry) @@ -280,13 +337,19 @@ class DomainSerializer(serializers.ModelSerializer): model = Domain fields = ('name', 'extension', 'cname', 'cname_entry') - def get_zone_name(self, obj): + @staticmethod + def get_zone_name(obj): + """ The name of the associated zone """ return obj.extension.name - def get_alias_name(self, obj): + @staticmethod + def get_alias_name(obj): + """ The name of the associated alias """ return str(obj.cname) - def get_cname_name(self, obj): + @staticmethod + def get_cname_name(obj): + """ The name of the associated CNAME target """ return str(obj.dns_entry) @@ -300,13 +363,19 @@ class ServiceServersSerializer(serializers.ModelSerializer): model = Service_link fields = ('server', 'service', 'need_regen') - def get_server_name(self, obj): + @staticmethod + def get_server_name(obj): + """ The name of the associated server """ return str(obj.server.domain.name) - def get_service_name(self, obj): + @staticmethod + def get_service_name(obj): + """ The name of the service name """ return str(obj.service) - def get_regen_status(self, obj): + @staticmethod + def get_regen_status(obj): + """ The string representation of the regen status """ return obj.need_regen() @@ -315,7 +384,19 @@ class OuverturePortsSerializer(serializers.Serializer): ipv4 = serializers.SerializerMethodField() ipv6 = serializers.SerializerMethodField() + def create(self, validated_data): + """ Creates a new object based on the un-serialized data. + Used to implement an abstract inherited method """ + pass + + def update(self, instance, validated_data): + """ Updates an object based on the un-serialized data. + Used to implement an abstract inherited method """ + pass + + @staticmethod def get_ipv4(): + """ The representation of the policy for the IPv4 addresses """ return { i.ipv4.ipv4: { "tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()], @@ -326,7 +407,9 @@ class OuverturePortsSerializer(serializers.Serializer): for i in Interface.objects.all() if i.ipv4 } + @staticmethod def get_ipv6(): + """ The representation of the policy for the IPv6 addresses """ return { i.ipv6: { "tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()], diff --git a/machines/tests.py b/machines/tests.py index dedc0021..2074f683 100644 --- a/machines/tests.py +++ b/machines/tests.py @@ -20,7 +20,10 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""machines.tests +The tests for the API module. +""" -from django.test import TestCase +# from django.test import TestCase # Create your tests here. diff --git a/machines/urls.py b/machines/urls.py index c9afd56c..9a5fa25e 100644 --- a/machines/urls.py +++ b/machines/urls.py @@ -20,6 +20,9 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""machines.urls +The defined URLs for the Cotisations app +""" from __future__ import unicode_literals diff --git a/machines/views.py b/machines/views.py index 3ea0cd11..d3f8f2a7 100644 --- a/machines/views.py +++ b/machines/views.py @@ -25,24 +25,41 @@ # App de gestion des machines pour re2o # Gabriel Détraz, Augustin Lemesle # Gplv2 +"""machines.views +The views for the Machines app +""" from __future__ import unicode_literals from django.urls import reverse from django.http import HttpResponse from django.shortcuts import render, redirect -from django.shortcuts import get_object_or_404 -from django.template.context_processors import csrf -from django.template import Context, RequestContext, loader from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required from django.db.models import ProtectedError, F -from django.forms import ValidationError, modelformset_factory -from django.contrib.auth import authenticate, login +from django.forms import modelformset_factory from django.views.decorators.csrf import csrf_exempt from rest_framework.renderers import JSONRenderer -from machines.serializers import ( + +from users.models import User +from preferences.models import GeneralOption +from re2o.utils import ( + all_active_assigned_interfaces, + filter_active_interfaces, + SortTable, + re2o_paginator, +) +from re2o.acl import ( + can_create, + can_edit, + can_delete, + can_view_all, + can_delete_set, +) +from re2o.views import form + +from .serializers import ( FullInterfaceSerializer, InterfaceSerializer, TypeSerializer, @@ -53,12 +70,8 @@ from machines.serializers import ( ExtensionSerializer, ServiceServersSerializer, NsSerializer, - OuverturePortsSerializer ) -from reversion import revisions as reversion -from reversion.models import Version -import re from .forms import ( NewMachineForm, EditMachineForm, @@ -68,8 +81,6 @@ from .forms import ( DelMachineTypeForm, ExtensionForm, DelExtensionForm, -) -from .forms import ( EditIpTypeForm, IpTypeForm, DelIpTypeForm, @@ -93,13 +104,13 @@ from .forms import ( SrvForm, DelSrvForm, Ipv6ListForm, + EditOuverturePortListForm, + EditOuverturePortConfigForm ) -from .forms import EditOuverturePortListForm, EditOuverturePortConfigForm from .models import ( IpType, Machine, Interface, - IpList, MachineType, Extension, SOA, @@ -116,24 +127,6 @@ from .models import ( OuverturePort, Ipv6List, ) -from users.models import User -from preferences.models import GeneralOption, OptionalMachine -from re2o.utils import ( - all_active_assigned_interfaces, - all_has_access, - filter_active_interfaces, - SortTable, - re2o_paginator, -) -from re2o.acl import ( - can_create, - can_edit, - can_delete, - can_view, - can_view_all, - can_delete_set, -) -from re2o.views import form def f_type_id(is_type_tt): @@ -143,10 +136,10 @@ def f_type_id(is_type_tt): return 'id_Interface-type_hidden' if is_type_tt else 'id_Interface-type' -def generate_ipv4_choices(form): +def generate_ipv4_choices(form_obj): """ Generate the parameter choices for the massive_bootstrap_form tag """ - f_ipv4 = form.fields['ipv4'] + f_ipv4 = form_obj.fields['ipv4'] used_mtype_id = [] choices = '{"":[{key:"",value:"Choisissez d\'abord un type de machine"},' mtype_id = -1 @@ -166,7 +159,7 @@ def generate_ipv4_choices(form): v=ip.ipv4 ) - for t in form.fields['type'].queryset.exclude(id__in=used_mtype_id): + for t in form_obj.fields['type'].queryset.exclude(id__in=used_mtype_id): choices += '], "'+str(t.id)+'": [' choices += '{key: "", value: "' + str(f_ipv4.empty_label) + '"},' choices += ']}' @@ -206,10 +199,10 @@ def generate_ipv4_match_func(is_type_tt): ) -def generate_ipv4_mbf_param(form, is_type_tt): +def generate_ipv4_mbf_param(form_obj, is_type_tt): """ Generate all the parameters to use with the massive_bootstrap_form tag """ - i_choices = {'ipv4': generate_ipv4_choices(form)} + i_choices = {'ipv4': generate_ipv4_choices(form_obj)} i_engine = {'ipv4': generate_ipv4_engine(is_type_tt)} i_match_func = {'ipv4': generate_ipv4_match_func(is_type_tt)} i_update_on = {'ipv4': [f_type_id(is_type_tt)]} @@ -227,7 +220,7 @@ def generate_ipv4_mbf_param(form, is_type_tt): @login_required @can_create(Machine) @can_edit(User) -def new_machine(request, user, userid): +def new_machine(request, user, _userid): """ Fonction de creation d'une machine. Cree l'objet machine, le sous objet interface et l'objet domain à partir de model forms. Trop complexe, devrait être simplifié""" @@ -239,16 +232,16 @@ def new_machine(request, user, userid): ) domain = DomainForm(request.POST or None, user=user) if machine.is_valid() and interface.is_valid(): - new_machine = machine.save(commit=False) - new_machine.user = user - new_interface = interface.save(commit=False) - domain.instance.interface_parent = new_interface + new_machine_obj = machine.save(commit=False) + new_machine_obj.user = user + new_interface_obj = interface.save(commit=False) + domain.instance.interface_parent = new_interface_obj if domain.is_valid(): new_domain = domain.save(commit=False) - new_machine.save() - new_interface.machine = new_machine - new_interface.save() - new_domain.interface_parent = new_interface + new_machine_obj.save() + new_interface_obj.machine = new_machine_obj + new_interface_obj.save() + new_domain.interface_parent = new_interface_obj new_domain.save() messages.success(request, "La machine a été créée") return redirect(reverse( @@ -271,7 +264,7 @@ def new_machine(request, user, userid): @login_required @can_edit(Interface) -def edit_interface(request, interface_instance, interfaceid): +def edit_interface(request, interface_instance, _interfaceid): """ Edition d'une interface. Distingue suivant les droits les valeurs de interfaces et machines que l'user peut modifier infra permet de modifier le propriétaire""" @@ -293,15 +286,15 @@ def edit_interface(request, interface_instance, interfaceid): if (machine_form.is_valid() and interface_form.is_valid() and domain_form.is_valid()): - new_machine = machine_form.save(commit=False) - new_interface = interface_form.save(commit=False) - new_domain = domain_form.save(commit=False) + new_machine_obj = machine_form.save(commit=False) + new_interface_obj = interface_form.save(commit=False) + new_domain_obj = domain_form.save(commit=False) if machine_form.changed_data: - new_machine.save() + new_machine_obj.save() if interface_form.changed_data: - new_interface.save() + new_interface_obj.save() if domain_form.changed_data: - new_domain.save() + new_domain_obj.save() messages.success(request, "La machine a été modifiée") return redirect(reverse( 'users:profil', @@ -323,7 +316,7 @@ def edit_interface(request, interface_instance, interfaceid): @login_required @can_delete(Machine) -def del_machine(request, machine, machineid): +def del_machine(request, machine, _machineid): """ Supprime une machine, interfaces en mode cascade""" if request.method == "POST": machine.delete() @@ -342,20 +335,20 @@ def del_machine(request, machine, machineid): @login_required @can_create(Interface) @can_edit(Machine) -def new_interface(request, machine, machineid): +def new_interface(request, machine, _machineid): """ Ajoute une interface et son domain associé à une machine existante""" interface_form = AddInterfaceForm(request.POST or None, user=request.user) domain_form = DomainForm(request.POST or None) if interface_form.is_valid(): - new_interface = interface_form.save(commit=False) - domain_form.instance.interface_parent = new_interface - new_interface.machine = machine + new_interface_obj = interface_form.save(commit=False) + domain_form.instance.interface_parent = new_interface_obj + new_interface_obj.machine = machine if domain_form.is_valid(): - new_domain = domain_form.save(commit=False) - new_interface.save() - new_domain.interface_parent = new_interface - new_domain.save() + new_domain_obj = domain_form.save(commit=False) + new_interface_obj.save() + new_domain_obj.interface_parent = new_interface_obj + new_domain_obj.save() messages.success(request, "L'interface a été ajoutée") return redirect(reverse( 'users:profil', @@ -376,7 +369,7 @@ def new_interface(request, machine, machineid): @login_required @can_delete(Interface) -def del_interface(request, interface, interfaceid): +def del_interface(request, interface, _interfaceid): """ Supprime une interface. Domain objet en mode cascade""" if request.method == "POST": machine = interface.machine @@ -398,7 +391,7 @@ def del_interface(request, interface, interfaceid): @login_required @can_create(Ipv6List) @can_edit(Interface) -def new_ipv6list(request, interface, interfaceid): +def new_ipv6list(request, interface, _interfaceid): """Nouvelle ipv6""" ipv6list_instance = Ipv6List(interface=interface) ipv6 = Ipv6ListForm( @@ -422,7 +415,7 @@ def new_ipv6list(request, interface, interfaceid): @login_required @can_edit(Ipv6List) -def edit_ipv6list(request, ipv6list_instance, ipv6listid): +def edit_ipv6list(request, ipv6list_instance, _ipv6listid): """Edition d'une ipv6""" ipv6 = Ipv6ListForm( request.POST or None, @@ -446,7 +439,7 @@ def edit_ipv6list(request, ipv6list_instance, ipv6listid): @login_required @can_delete(Ipv6List) -def del_ipv6list(request, ipv6list, ipv6listid): +def del_ipv6list(request, ipv6list, _ipv6listid): """ Supprime une ipv6""" if request.method == "POST": interfaceid = ipv6list.interface.id @@ -483,7 +476,7 @@ def add_iptype(request): @login_required @can_edit(IpType) -def edit_iptype(request, iptype_instance, iptypeid): +def edit_iptype(request, iptype_instance, _iptypeid): """ Edition d'un range. Ne permet pas de le redimensionner pour éviter l'incohérence""" @@ -528,6 +521,7 @@ def del_iptype(request, instances): @login_required @can_create(MachineType) def add_machinetype(request): + """ View used to add a Machinetype object """ machinetype = MachineTypeForm(request.POST or None) if machinetype.is_valid(): machinetype.save() @@ -542,7 +536,8 @@ def add_machinetype(request): @login_required @can_edit(MachineType) -def edit_machinetype(request, machinetype_instance, machinetypeid): +def edit_machinetype(request, machinetype_instance, _machinetypeid): + """ View used to edit a MachineType object """ machinetype = MachineTypeForm( request.POST or None, instance=machinetype_instance @@ -562,6 +557,7 @@ def edit_machinetype(request, machinetype_instance, machinetypeid): @login_required @can_delete_set(MachineType) def del_machinetype(request, instances): + """ View used to delete a MachineType object """ machinetype = DelMachineTypeForm(request.POST or None, instances=instances) if machinetype.is_valid(): machinetype_dels = machinetype.cleaned_data['machinetypes'] @@ -587,6 +583,7 @@ def del_machinetype(request, instances): @login_required @can_create(Extension) def add_extension(request): + """ View used to add an Extension object """ extension = ExtensionForm(request.POST or None) if extension.is_valid(): extension.save() @@ -601,7 +598,8 @@ def add_extension(request): @login_required @can_edit(Extension) -def edit_extension(request, extension_instance, extensionid): +def edit_extension(request, extension_instance, _extensionid): + """ View used to edit an Extension object """ extension = ExtensionForm( request.POST or None, instance=extension_instance @@ -621,6 +619,7 @@ def edit_extension(request, extension_instance, extensionid): @login_required @can_delete_set(Extension) def del_extension(request, instances): + """ View used to delete an Extension object """ extension = DelExtensionForm(request.POST or None, instances=instances) if extension.is_valid(): extension_dels = extension.cleaned_data['extensions'] @@ -646,6 +645,7 @@ def del_extension(request, instances): @login_required @can_create(SOA) def add_soa(request): + """ View used to add a SOA object """ soa = SOAForm(request.POST or None) if soa.is_valid(): soa.save() @@ -660,7 +660,8 @@ def add_soa(request): @login_required @can_edit(SOA) -def edit_soa(request, soa_instance, soaid): +def edit_soa(request, soa_instance, _soaid): + """ View used to edit a SOA object """ soa = SOAForm(request.POST or None, instance=soa_instance) if soa.is_valid(): if soa.changed_data: @@ -677,6 +678,7 @@ def edit_soa(request, soa_instance, soaid): @login_required @can_delete_set(SOA) def del_soa(request, instances): + """ View used to delete a SOA object """ soa = DelSOAForm(request.POST or None, instances=instances) if soa.is_valid(): soa_dels = soa.cleaned_data['soa'] @@ -701,6 +703,7 @@ def del_soa(request, instances): @login_required @can_create(Mx) def add_mx(request): + """ View used to add a MX object """ mx = MxForm(request.POST or None) if mx.is_valid(): mx.save() @@ -715,7 +718,8 @@ def add_mx(request): @login_required @can_edit(Mx) -def edit_mx(request, mx_instance, mxid): +def edit_mx(request, mx_instance, _mxid): + """ View used to edit a MX object """ mx = MxForm(request.POST or None, instance=mx_instance) if mx.is_valid(): if mx.changed_data: @@ -732,6 +736,7 @@ def edit_mx(request, mx_instance, mxid): @login_required @can_delete_set(Mx) def del_mx(request, instances): + """ View used to delete a MX object """ mx = DelMxForm(request.POST or None, instances=instances) if mx.is_valid(): mx_dels = mx.cleaned_data['mx'] @@ -756,6 +761,7 @@ def del_mx(request, instances): @login_required @can_create(Ns) def add_ns(request): + """ View used to add a NS object """ ns = NsForm(request.POST or None) if ns.is_valid(): ns.save() @@ -770,7 +776,8 @@ def add_ns(request): @login_required @can_edit(Ns) -def edit_ns(request, ns_instance, nsid): +def edit_ns(request, ns_instance, _nsid): + """ View used to edit a NS object """ ns = NsForm(request.POST or None, instance=ns_instance) if ns.is_valid(): if ns.changed_data: @@ -787,6 +794,7 @@ def edit_ns(request, ns_instance, nsid): @login_required @can_delete_set(Ns) def del_ns(request, instances): + """ View used to delete a NS object """ ns = DelNsForm(request.POST or None, instances=instances) if ns.is_valid(): ns_dels = ns.cleaned_data['ns'] @@ -811,6 +819,7 @@ def del_ns(request, instances): @login_required @can_create(Txt) def add_txt(request): + """ View used to add a TXT object """ txt = TxtForm(request.POST or None) if txt.is_valid(): txt.save() @@ -825,7 +834,8 @@ def add_txt(request): @login_required @can_edit(Txt) -def edit_txt(request, txt_instance, txtid): +def edit_txt(request, txt_instance, _txtid): + """ View used to edit a TXT object """ txt = TxtForm(request.POST or None, instance=txt_instance) if txt.is_valid(): if txt.changed_data: @@ -842,6 +852,7 @@ def edit_txt(request, txt_instance, txtid): @login_required @can_delete_set(Txt) def del_txt(request, instances): + """ View used to delete a TXT object """ txt = DelTxtForm(request.POST or None, instances=instances) if txt.is_valid(): txt_dels = txt.cleaned_data['txt'] @@ -866,6 +877,7 @@ def del_txt(request, instances): @login_required @can_create(Srv) def add_srv(request): + """ View used to add a SRV object """ srv = SrvForm(request.POST or None) if srv.is_valid(): srv.save() @@ -880,7 +892,8 @@ def add_srv(request): @login_required @can_edit(Srv) -def edit_srv(request, srv_instance, srvid): +def edit_srv(request, srv_instance, _srvid): + """ View used to edit a SRV object """ srv = SrvForm(request.POST or None, instance=srv_instance) if srv.is_valid(): if srv.changed_data: @@ -897,6 +910,7 @@ def edit_srv(request, srv_instance, srvid): @login_required @can_delete_set(Srv) def del_srv(request, instances): + """ View used to delete a SRV object """ srv = DelSrvForm(request.POST or None, instances=instances) if srv.is_valid(): srv_dels = srv.cleaned_data['srv'] @@ -922,6 +936,7 @@ def del_srv(request, instances): @can_create(Domain) @can_edit(Interface) def add_alias(request, interface, interfaceid): + """ View used to add an Alias object """ alias = AliasForm(request.POST or None, user=request.user) if alias.is_valid(): alias = alias.save(commit=False) @@ -941,7 +956,8 @@ def add_alias(request, interface, interfaceid): @login_required @can_edit(Domain) -def edit_alias(request, domain_instance, domainid): +def edit_alias(request, domain_instance, _domainid): + """ View used to edit an Alias object """ alias = AliasForm( request.POST or None, instance=domain_instance, @@ -967,6 +983,7 @@ def edit_alias(request, domain_instance, domainid): @login_required @can_edit(Interface) def del_alias(request, interface, interfaceid): + """ View used to delete an Alias object """ alias = DelAliasForm(request.POST or None, interface=interface) if alias.is_valid(): alias_dels = alias.cleaned_data['alias'] @@ -997,6 +1014,7 @@ def del_alias(request, interface, interfaceid): @login_required @can_create(Service) def add_service(request): + """ View used to add a Service object """ service = ServiceForm(request.POST or None) if service.is_valid(): service.save() @@ -1011,7 +1029,8 @@ def add_service(request): @login_required @can_edit(Service) -def edit_service(request, service_instance, serviceid): +def edit_service(request, service_instance, _serviceid): + """ View used to edit a Service object """ service = ServiceForm(request.POST or None, instance=service_instance) if service.is_valid(): if service.changed_data: @@ -1028,6 +1047,7 @@ def edit_service(request, service_instance, serviceid): @login_required @can_delete_set(Service) def del_service(request, instances): + """ View used to delete a Service object """ service = DelServiceForm(request.POST or None, instances=instances) if service.is_valid(): service_dels = service.cleaned_data['service'] @@ -1052,6 +1072,7 @@ def del_service(request, instances): @login_required @can_create(Vlan) def add_vlan(request): + """ View used to add a VLAN object """ vlan = VlanForm(request.POST or None) if vlan.is_valid(): vlan.save() @@ -1066,7 +1087,8 @@ def add_vlan(request): @login_required @can_edit(Vlan) -def edit_vlan(request, vlan_instance, vlanid): +def edit_vlan(request, vlan_instance, _vlanid): + """ View used to edit a VLAN object """ vlan = VlanForm(request.POST or None, instance=vlan_instance) if vlan.is_valid(): if vlan.changed_data: @@ -1083,6 +1105,7 @@ def edit_vlan(request, vlan_instance, vlanid): @login_required @can_delete_set(Vlan) def del_vlan(request, instances): + """ View used to delete a VLAN object """ vlan = DelVlanForm(request.POST or None, instances=instances) if vlan.is_valid(): vlan_dels = vlan.cleaned_data['vlan'] @@ -1107,6 +1130,7 @@ def del_vlan(request, instances): @login_required @can_create(Nas) def add_nas(request): + """ View used to add a NAS object """ nas = NasForm(request.POST or None) if nas.is_valid(): nas.save() @@ -1121,7 +1145,8 @@ def add_nas(request): @login_required @can_edit(Nas) -def edit_nas(request, nas_instance, nasid): +def edit_nas(request, nas_instance, _nasid): + """ View used to edit a NAS object """ nas = NasForm(request.POST or None, instance=nas_instance) if nas.is_valid(): if nas.changed_data: @@ -1138,6 +1163,7 @@ def edit_nas(request, nas_instance, nasid): @login_required @can_delete_set(Nas) def del_nas(request, instances): + """ View used to delete a NAS object """ nas = DelNasForm(request.POST or None, instances=instances) if nas.is_valid(): nas_dels = nas.cleaned_data['nas'] @@ -1162,6 +1188,8 @@ def del_nas(request, instances): @login_required @can_view_all(Machine) def index(request): + """ The home view for this app. Displays the list of registered + machines in Re2o """ pagination_large_number = (GeneralOption .get_cached_value('pagination_large_number')) machines_list = (Machine.objects @@ -1194,6 +1222,7 @@ def index(request): @login_required @can_view_all(IpType) def index_iptype(request): + """ View displaying the list of existing types of IP """ iptype_list = (IpType.objects .select_related('extension') .select_related('vlan') @@ -1208,6 +1237,7 @@ def index_iptype(request): @login_required @can_view_all(Vlan) def index_vlan(request): + """ View displaying the list of existing VLANs """ vlan_list = Vlan.objects.prefetch_related('iptype_set').order_by('vlan_id') return render( request, @@ -1219,6 +1249,7 @@ def index_vlan(request): @login_required @can_view_all(MachineType) def index_machinetype(request): + """ View displaying the list of existing types of machines """ machinetype_list = (MachineType.objects .select_related('ip_type') .order_by('type')) @@ -1232,6 +1263,7 @@ def index_machinetype(request): @login_required @can_view_all(Nas) def index_nas(request): + """ View displaying the list of existing NAS """ nas_list = (Nas.objects .select_related('machine_type') .select_related('nas_type') @@ -1247,6 +1279,10 @@ def index_nas(request): @can_view_all(Srv) @can_view_all(Extension) def index_extension(request): + """ View displaying the list of existing extensions, the list of + existing SOA records, the list of existing MX records , the list of + existing NS records, the list of existing TXT records and the list of + existing SRV records """ extension_list = (Extension.objects .select_related('origin') .select_related('soa') @@ -1282,6 +1318,7 @@ def index_extension(request): @login_required @can_edit(Interface) def index_alias(request, interface, interfaceid): + """ View used to display the list of existing alias of an interface """ alias_list = Domain.objects.filter( cname=Domain.objects.filter(interface_parent=interface) ).order_by('name') @@ -1295,6 +1332,7 @@ def index_alias(request, interface, interfaceid): @login_required @can_edit(Interface) def index_ipv6(request, interface, interfaceid): + """ View used to display the list of existing IPv6 of an interface """ ipv6_list = Ipv6List.objects.filter(interface=interface) return render( request, @@ -1306,6 +1344,7 @@ def index_ipv6(request, interface, interfaceid): @login_required @can_view_all(Service) def index_service(request): + """ View used to display the list of existing services """ service_list = (Service.objects .prefetch_related( 'service_link_set__server__domain__extension' @@ -1324,6 +1363,7 @@ def index_service(request): @login_required @can_view_all(OuverturePortList) def index_portlist(request): + """ View used to display the list of existing port policies """ port_list = (OuverturePortList.objects .prefetch_related('ouvertureport_set') .prefetch_related('interface_set__domain__extension') @@ -1338,7 +1378,8 @@ def index_portlist(request): @login_required @can_edit(OuverturePortList) -def edit_portlist(request, ouvertureportlist_instance, ouvertureportlistid): +def edit_portlist(request, ouvertureportlist_instance, _ouvertureportlistid): + """ View used to edit a port policy """ port_list = EditOuverturePortListForm( request.POST or None, instance=ouvertureportlist_instance @@ -1376,7 +1417,8 @@ def edit_portlist(request, ouvertureportlist_instance, ouvertureportlistid): @login_required @can_delete(OuverturePortList) -def del_portlist(request, port_list_instance, ouvertureportlistid): +def del_portlist(request, port_list_instance, _ouvertureportlistid): + """ View used to delete a port policy """ port_list_instance.delete() messages.success(request, "La liste de ports a été supprimée") return redirect(reverse('machines:index-portlist')) @@ -1385,6 +1427,7 @@ def del_portlist(request, port_list_instance, ouvertureportlistid): @login_required @can_create(OuverturePortList) def add_portlist(request): + """ View used to add a port policy """ port_list = EditOuverturePortListForm(request.POST or None) port_formset = modelformset_factory( OuverturePort, @@ -1409,22 +1452,14 @@ def add_portlist(request): 'machines/edit_portlist.html', request ) - port_list = EditOuverturePortListForm(request.POST or None) - if port_list.is_valid(): - port_list.save() - messages.success(request, "Liste de ports créée") - return redirect(reverse('machines:index-portlist')) - return form( - {'machineform': port_list, 'action_name': 'Créer'}, - 'machines/machine.html', - request - ) @login_required @can_create(OuverturePort) @can_edit(Interface) -def configure_ports(request, interface_instance, interfaceid): +def configure_ports(request, interface_instance, _interfaceid): + """ View to display the list of configured port policy for an + interface """ if not interface_instance.may_have_port_open(): messages.error( request, @@ -1447,10 +1482,11 @@ def configure_ports(request, interface_instance, interfaceid): ) -""" Framework Rest """ +## Framework Rest class JSONResponse(HttpResponse): + """ Class to build a JSON response. Used for API """ def __init__(self, data, **kwargs): content = JSONRenderer().render(data) kwargs['content_type'] = 'application/json' @@ -1460,7 +1496,8 @@ class JSONResponse(HttpResponse): @csrf_exempt @login_required @permission_required('machines.serveur') -def mac_ip_list(request): +def mac_ip_list(_request): + """ API view to list the active and assigned interfaces """ interfaces = all_active_assigned_interfaces() seria = InterfaceSerializer(interfaces, many=True) return seria.data @@ -1469,7 +1506,9 @@ def mac_ip_list(request): @csrf_exempt @login_required @permission_required('machines.serveur') -def full_mac_ip_list(request): +def full_mac_ip_list(_request): + """ API view to list the active and assigned interfaces. More + detailed than mac_ip_list(request) """ interfaces = all_active_assigned_interfaces(full=True) seria = FullInterfaceSerializer(interfaces, many=True) return seria.data @@ -1478,7 +1517,8 @@ def full_mac_ip_list(request): @csrf_exempt @login_required @permission_required('machines.serveur') -def alias(request): +def alias(_request): + """ API view to list the alias (CNAME) for all assigned interfaces """ alias = (Domain.objects .filter(interface_parent=None) .filter( @@ -1494,7 +1534,8 @@ def alias(request): @csrf_exempt @login_required @permission_required('machines.serveur') -def corresp(request): +def corresp(_request): + """ API view to list the types of IP and infos about it """ type = IpType.objects.all().select_related('extension') seria = TypeSerializer(type, many=True) return JSONResponse(seria.data) @@ -1503,7 +1544,8 @@ def corresp(request): @csrf_exempt @login_required @permission_required('machines.serveur') -def mx(request): +def mx(_request): + """ API view to list the MX records """ mx = (Mx.objects.all() .select_related('zone') .select_related('name__extension')) @@ -1514,7 +1556,8 @@ def mx(request): @csrf_exempt @login_required @permission_required('machines.serveur') -def txt(request): +def txt(_request): + """ API view to list the TXT records """ txt = Txt.objects.all().select_related('zone') seria = TxtSerializer(txt, many=True) return JSONResponse(seria.data) @@ -1523,7 +1566,8 @@ def txt(request): @csrf_exempt @login_required @permission_required('machines.serveur') -def srv(request): +def srv(_request): + """ API view to list the SRV records """ srv = (Srv.objects .all() .select_related('extension') @@ -1535,7 +1579,8 @@ def srv(request): @csrf_exempt @login_required @permission_required('machines.serveur') -def ns(request): +def ns(_request): + """ API view to list the NS records """ ns = (Ns.objects .exclude( ns__in=Domain.objects.filter( @@ -1550,7 +1595,8 @@ def ns(request): @csrf_exempt @login_required @permission_required('machines.serveur') -def zones(request): +def zones(_request): + """ API view to list the DNS zones """ zones = Extension.objects.all().select_related('origin') seria = ExtensionSerializer(zones, many=True) return JSONResponse(seria.data) @@ -1560,6 +1606,7 @@ def zones(request): @login_required @permission_required('machines.serveur') def mac_ip(request): + """ API view to list the active and assigned interfaces """ seria = mac_ip_list(request) return JSONResponse(seria) @@ -1568,6 +1615,8 @@ def mac_ip(request): @login_required @permission_required('machines.serveur') def mac_ip_dns(request): + """ API view to list the active and assigned interfaces. More + detailed than mac_ip_list(request) """ seria = full_mac_ip_list(request) return JSONResponse(seria) @@ -1575,7 +1624,8 @@ def mac_ip_dns(request): @csrf_exempt @login_required @permission_required('machines.serveur') -def service_servers(request): +def service_servers(_request): + """ API view to list the service links """ service_link = (Service_link.objects .all() .select_related('server__domain') @@ -1587,7 +1637,8 @@ def service_servers(request): @csrf_exempt @login_required @permission_required('machines.serveur') -def ouverture_ports(request): +def ouverture_ports(_request): + """ API view to list the port policies for each IP """ r = {'ipv4': {}, 'ipv6': {}} for o in (OuverturePortList.objects .all() @@ -1654,6 +1705,8 @@ def ouverture_ports(request): @login_required @permission_required('machines.serveur') def regen_achieved(request): + """ API view to list the regen status for each (Service link, Server) + couple """ obj = (Service_link.objects .filter( service__in=Service.objects.filter( From 332e8a3413afea8b8f61683c2de4a5ce6f96e68f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 18:55:24 +0000 Subject: [PATCH 14/22] Pylint compliance on preferences --- preferences/__init__.py | 26 ++++++++++++++++++++++ preferences/aes_field.py | 47 +++++++++++++++++++++++++++++++++------- preferences/apps.py | 5 ----- preferences/models.py | 25 +++++++++++++-------- preferences/tests.py | 28 +++++++++++++++++++++++- preferences/urls.py | 2 +- preferences/views.py | 8 +++---- 7 files changed, 113 insertions(+), 28 deletions(-) delete mode 100644 preferences/apps.py diff --git a/preferences/__init__.py b/preferences/__init__.py index e895e295..c287fce8 100644 --- a/preferences/__init__.py +++ b/preferences/__init__.py @@ -1,2 +1,28 @@ +# -*- mode: python; coding: utf-8 -*- +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# Copyright © 2018 Maël Kervella +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""preferences +The app in charge of storing all the preferences for the local installation +""" from .acl import * diff --git a/preferences/aes_field.py b/preferences/aes_field.py index ce90e1f4..1d3ffa54 100644 --- a/preferences/aes_field.py +++ b/preferences/aes_field.py @@ -1,3 +1,34 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# Copyright © 2018 Maël Kervella +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# App de gestion des machines pour re2o +# Gabriel Détraz, Augustin Lemesle +# Gplv2 +"""preferences.aes_field +Module defining a AESEncryptedField object that can be used in forms +to handle the use of properly encrypting and decrypting AES keys +""" + import string import binascii from random import choice @@ -10,10 +41,13 @@ EOD = '`%EofD%`' # This should be something that will not occur in strings def genstring(length=16, chars=string.printable): + """ Generate a random string of length `length` and composed of + the characters in `chars` """ return ''.join([choice(chars) for i in range(length)]) def encrypt(key, s): + """ AES Encrypt a secret `s` with the key `key` """ obj = AES.new(key) datalength = len(s) + len(EOD) if datalength < 16: @@ -25,12 +59,15 @@ def encrypt(key, s): def decrypt(key, s): + """ AES Decrypt a secret `s` with the key `key` """ obj = AES.new(key) ss = obj.decrypt(s) return ss.split(bytes(EOD, 'utf-8'))[0] class AESEncryptedField(models.CharField): + """ A Field that can be used in forms for adding the support + of AES ecnrypted fields """ def save_form_data(self, instance, data): setattr(instance, self.name, binascii.b2a_base64(encrypt(settings.AES_KEY, data))) @@ -41,16 +78,10 @@ class AESEncryptedField(models.CharField): return decrypt(settings.AES_KEY, binascii.a2b_base64(value)).decode('utf-8') - def from_db_value(self, value, expression, connection, *args): - if value is None: - return value - return decrypt(settings.AES_KEY, - binascii.a2b_base64(value)).decode('utf-8') - def get_prep_value(self, value): if value is None: return value return binascii.b2a_base64(encrypt( - settings.AES_KEY, - value + settings.AES_KEY, + value )) diff --git a/preferences/apps.py b/preferences/apps.py deleted file mode 100644 index 85238099..00000000 --- a/preferences/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class PreferencesConfig(AppConfig): - name = 'preferences' diff --git a/preferences/models.py b/preferences/models.py index c5363fdd..4495f629 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -27,25 +27,31 @@ from __future__ import unicode_literals from django.utils.functional import cached_property from django.db import models -import cotisations.models -import machines.models -from django.db.models.signals import post_save, post_delete +from django.db.models.signals import post_save from django.dispatch import receiver from django.core.cache import cache -from .aes_field import AESEncryptedField +import cotisations.models +import machines.models from re2o.mixins import AclMixin +from .aes_field import AESEncryptedField + class PreferencesModel(models.Model): + """ Base object for the Preferences objects + Defines methods to handle the cache of the settings (they should + not change a lot) """ @classmethod def set_in_cache(cls): + """ Save the preferences in a server-side cache """ instance, _created = cls.objects.get_or_create() cache.set(cls().__class__.__name__.lower(), instance, None) return instance @classmethod def get_cached_value(cls, key): + """ Get the preferences from the server-side cache """ instance = cache.get(cls().__class__.__name__.lower()) if instance is None: instance = cls.set_in_cache() @@ -112,7 +118,7 @@ class OptionalUser(AclMixin, PreferencesModel): @receiver(post_save, sender=OptionalUser) -def optionaluser_post_save(sender, **kwargs): +def optionaluser_post_save(_sender, **kwargs): """Ecriture dans le cache""" user_pref = kwargs['instance'] user_pref.set_in_cache() @@ -147,6 +153,7 @@ class OptionalMachine(AclMixin, PreferencesModel): @cached_property def ipv6(self): + """ Check if the IPv6 option is activated """ return not self.get_cached_value('ipv6_mode') == 'DISABLED' class Meta: @@ -156,7 +163,7 @@ class OptionalMachine(AclMixin, PreferencesModel): @receiver(post_save, sender=OptionalMachine) -def optionalmachine_post_save(sender, **kwargs): +def optionalmachine_post_save(_sender, **kwargs): """Synchronisation ipv6 et ecriture dans le cache""" machine_pref = kwargs['instance'] machine_pref.set_in_cache() @@ -204,7 +211,7 @@ class OptionalTopologie(AclMixin, PreferencesModel): @receiver(post_save, sender=OptionalTopologie) -def optionaltopologie_post_save(sender, **kwargs): +def optionaltopologie_post_save(_sender, **kwargs): """Ecriture dans le cache""" topologie_pref = kwargs['instance'] topologie_pref.set_in_cache() @@ -244,7 +251,7 @@ class GeneralOption(AclMixin, PreferencesModel): @receiver(post_save, sender=GeneralOption) -def generaloption_post_save(sender, **kwargs): +def generaloption_post_save(_sender, **kwargs): """Ecriture dans le cache""" general_pref = kwargs['instance'] general_pref.set_in_cache() @@ -318,7 +325,7 @@ class AssoOption(AclMixin, PreferencesModel): @receiver(post_save, sender=AssoOption) -def assooption_post_save(sender, **kwargs): +def assooption_post_save(_sender, **kwargs): """Ecriture dans le cache""" asso_pref = kwargs['instance'] asso_pref.set_in_cache() diff --git a/preferences/tests.py b/preferences/tests.py index 7ce503c2..8b980c73 100644 --- a/preferences/tests.py +++ b/preferences/tests.py @@ -1,3 +1,29 @@ -from django.test import TestCase +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# Copyright © 2018 Maël Kervella +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""preferences.tests +The tests for the Preferences module. +""" + +# from django.test import TestCase # Create your tests here. diff --git a/preferences/urls.py b/preferences/urls.py index d8aba5b9..9b51a432 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -27,8 +27,8 @@ from __future__ import unicode_literals from django.conf.urls import url -from . import views import re2o +from . import views urlpatterns = [ diff --git a/preferences/views.py b/preferences/views.py index aa065d1d..d7971b98 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -31,17 +31,17 @@ topologie, users, service...) from __future__ import unicode_literals from django.urls import reverse -from django.shortcuts import render, redirect +from django.shortcuts import redirect from django.contrib import messages -from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.decorators import login_required from django.db.models import ProtectedError from django.db import transaction -from reversion.models import Version from reversion import revisions as reversion from re2o.views import form from re2o.acl import can_create, can_edit, can_delete_set, can_view_all + from .forms import ServiceForm, DelServiceForm from .models import Service, OptionalUser, OptionalMachine, AssoOption from .models import MailMessageOption, GeneralOption, OptionalTopologie @@ -136,7 +136,7 @@ def add_service(request): @login_required @can_edit(Service) -def edit_service(request, service_instance, serviceid): +def edit_service(request, service_instance, _serviceid): """Edition des services affichés sur la page d'accueil""" service = ServiceForm(request.POST or None, instance=service_instance) if service.is_valid(): From 2ca271bf828865cd531d5cb09b78ffb554ba2400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 19:29:16 +0000 Subject: [PATCH 15/22] Pylint compliance on re2o --- re2o/__init__.py | 12 ++ re2o/acl.py | 139 +++++++++++++++--------- re2o/contributors.py | 4 +- re2o/field_permissions.py | 50 ++++++--- re2o/login.py | 23 ++-- re2o/management/commands/gen_contrib.py | 7 +- re2o/mixins.py | 25 ++++- re2o/script_utils.py | 19 ++-- re2o/settings.py | 100 +++++++++-------- re2o/settings_local.example.py | 47 +++++--- re2o/templatetags/acl.py | 2 +- re2o/templatetags/self_adhesion.py | 9 +- re2o/utils.py | 9 +- re2o/views.py | 27 +++-- re2o/wsgi.py | 3 +- 15 files changed, 295 insertions(+), 181 deletions(-) diff --git a/re2o/__init__.py b/re2o/__init__.py index cd256e09..eda09716 100644 --- a/re2o/__init__.py +++ b/re2o/__init__.py @@ -20,3 +20,15 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""re2o +The main app of Re2o. In charge of all the basics elements which are not +specific to anyother apps. It includes : + * Templates used in multiple places + * Templatetags used in multiple places + * ACL base + * Mixins base + * Settings for the Django project + * The login part + * Some utility scripts + * ... +""" diff --git a/re2o/acl.py b/re2o/acl.py index a4c0027c..0acc710a 100644 --- a/re2o/acl.py +++ b/re2o/acl.py @@ -33,14 +33,6 @@ from django.contrib import messages from django.shortcuts import redirect from django.urls import reverse -import cotisations -import logs -import machines -import preferences -import search -import topologie -import users - def can_create(model): """Decorator to check if an user can create a model. @@ -49,7 +41,11 @@ def can_create(model): of models. """ def decorator(view): + """The decorator to use on a specific view + """ def wrapper(request, *args, **kwargs): + """The wrapper used for a specific request + """ can, msg = model.can_create(request.user, *args, **kwargs) if not can: messages.error( @@ -68,31 +64,37 @@ def can_edit(model, *field_list): kind of models. """ def decorator(view): + """The decorator to use on a specific view + """ def wrapper(request, *args, **kwargs): + """The wrapper used for a specific request + """ try: instance = model.get_instance(*args, **kwargs) except model.DoesNotExist: messages.error(request, u"Entrée inexistante") - return redirect(reverse('users:profil', - kwargs={'userid': str(request.user.id)} - )) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) can, msg = instance.can_edit(request.user) if not can: messages.error( request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid': str(request.user.id)} - )) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) for field in field_list: - can_change = getattr(instance, 'can_change_' + field) - can, msg = can_change(request.user, *args, **kwargs) + can_change_fct = getattr(instance, 'can_change_' + field) + can, msg = can_change_fct(request.user, *args, **kwargs) if not can: messages.error( request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid': str( - request.user.id)} - )) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) return view(request, instance, *args, **kwargs) return wrapper return decorator @@ -103,17 +105,21 @@ def can_change(model, *field_list): Difference with can_edit : take a class and not an instance """ def decorator(view): + """The decorator to use on a specific view + """ def wrapper(request, *args, **kwargs): + """The wrapper used for a specific request + """ for field in field_list: - can_change = getattr(model, 'can_change_' + field) - can, msg = can_change(request.user, *args, **kwargs) + can_change_fct = getattr(model, 'can_change_' + field) + can, msg = can_change_fct(request.user, *args, **kwargs) if not can: messages.error( request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid': str( - request.user.id)} - )) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) return view(request, *args, **kwargs) return wrapper return decorator @@ -127,21 +133,27 @@ def can_delete(model): kind of models. """ def decorator(view): + """The decorator to use on a specific view + """ def wrapper(request, *args, **kwargs): + """The wrapper used for a specific request + """ try: instance = model.get_instance(*args, **kwargs) except model.DoesNotExist: messages.error(request, u"Entrée inexistante") - return redirect(reverse('users:profil', - kwargs={'userid': str(request.user.id)} - )) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) can, msg = instance.can_delete(request.user) if not can: messages.error( request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid': str(request.user.id)} - )) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) return view(request, instance, *args, **kwargs) return wrapper return decorator @@ -151,7 +163,11 @@ def can_delete_set(model): """Decorator which returns a list of detable models by request user. If none of them, return an error""" def decorator(view): + """The decorator to use on a specific view + """ def wrapper(request, *args, **kwargs): + """The wrapper used for a specific request + """ all_objects = model.objects.all() instances_id = [] for instance in all_objects: @@ -160,10 +176,12 @@ def can_delete_set(model): instances_id.append(instance.id) instances = model.objects.filter(id__in=instances_id) if not instances: - messages.error(request, "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid': str(request.user.id)} - )) + messages.error( + request, msg or "Vous ne pouvez pas accéder à ce menu") + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) return view(request, instances, *args, **kwargs) return wrapper return decorator @@ -177,21 +195,27 @@ def can_view(model): kind of models. """ def decorator(view): + """The decorator to use on a specific view + """ def wrapper(request, *args, **kwargs): + """The wrapper used for a specific request + """ try: instance = model.get_instance(*args, **kwargs) except model.DoesNotExist: messages.error(request, u"Entrée inexistante") - return redirect(reverse('users:profil', - kwargs={'userid': str(request.user.id)} - )) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) can, msg = instance.can_view(request.user) if not can: messages.error( request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid': str(request.user.id)} - )) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) return view(request, instance, *args, **kwargs) return wrapper return decorator @@ -201,14 +225,19 @@ def can_view_all(model): """Decorator to check if an user can view a class of model. """ def decorator(view): + """The decorator to use on a specific view + """ def wrapper(request, *args, **kwargs): + """The wrapper used for a specific request + """ can, msg = model.can_view_all(request.user) if not can: messages.error( request, msg or "Vous ne pouvez pas accéder à ce menu") - return redirect(reverse('users:profil', - kwargs={'userid': str(request.user.id)} - )) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) return view(request, *args, **kwargs) return wrapper return decorator @@ -220,15 +249,20 @@ def can_view_app(app_name): assert app_name in sys.modules.keys() def decorator(view): + """The decorator to use on a specific view + """ def wrapper(request, *args, **kwargs): + """The wrapper used for a specific request + """ app = sys.modules[app_name] can, msg = app.can_view(request.user) if can: return view(request, *args, **kwargs) messages.error(request, msg) - return redirect(reverse('users:profil', - kwargs={'userid': str(request.user.id)} - )) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) return wrapper return decorator @@ -236,13 +270,16 @@ def can_view_app(app_name): def can_edit_history(view): """Decorator to check if an user can edit history.""" def wrapper(request, *args, **kwargs): + """The wrapper used for a specific request + """ if request.user.has_perm('admin.change_logentry'): return view(request, *args, **kwargs) messages.error( request, "Vous ne pouvez pas éditer l'historique." ) - return redirect(reverse('users:profil', - kwargs={'userid': str(request.user.id)} - )) + return redirect(reverse( + 'users:profil', + kwargs={'userid': str(request.user.id)} + )) return wrapper diff --git a/re2o/contributors.py b/re2o/contributors.py index e5af0a64..951acc47 100644 --- a/re2o/contributors.py +++ b/re2o/contributors.py @@ -1,4 +1,6 @@ -#!/usr/bin/env python3 +"""re2o.contributors +A list of the proud contributors to Re2o +""" CONTRIBUTORS = [ 'Gabriel "Chirac" Détraz', diff --git a/re2o/field_permissions.py b/re2o/field_permissions.py index e4c36552..5febeb63 100644 --- a/re2o/field_permissions.py +++ b/re2o/field_permissions.py @@ -1,15 +1,45 @@ -from django.db import models -from django import forms -from functools import partial - +# -*- mode: python; coding: utf-8 -*- +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Goulven Kermarec +# Copyright © 2017 Augustin Lemesle +# Copyright © 2018 Maël Kervella +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# 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., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""re2o.field_permissions +A model mixin and a field mixin used to remove some unauthorized fields +from the form automatically generated from the model. The model must +subclass `FieldPermissionModelMixin` and the form must subclass +`FieldPermissionFieldMixin` so when a Django form is generated from the +fields of the models, some fields will be removed if the user don't have +the rights to change them (can_change_{name}) +""" class FieldPermissionModelMixin: + """ The model mixin. Defines the `has_field_perm` function """ field_permissions = {} # {'field_name': callable} FIELD_PERM_CODENAME = 'can_change_{model}_{name}' FIELD_PERMISSION_GETTER = 'can_change_{name}' FIELD_PERMISSION_MISSING_DEFAULT = True def has_field_perm(self, user, field): + """ Checks if a `user` has the right to edit the `field` + of this model """ if field in self.field_permissions: checks = self.field_permissions[field] if not isinstance(checks, (list, tuple)): @@ -39,7 +69,7 @@ class FieldPermissionModelMixin: # Try to find a user setting that qualifies them for permission. for perm in checks: if callable(perm): - result, reason = perm(user_request=user) + result, _reason = perm(user_request=user) if result is not None: return result else: @@ -52,11 +82,6 @@ class FieldPermissionModelMixin: return False -class FieldPermissionModel(FieldPermissionModelMixin, models.Model): - class Meta: - abstract = True - - class FieldPermissionFormMixin: """ Construit le formulaire et retire les champs interdits @@ -73,8 +98,5 @@ class FieldPermissionFormMixin: self.remove_unauthorized_field(name) def remove_unauthorized_field(self, name): + """ Remove one field from the fields of the form """ del self.fields[name] - - -class FieldPermissionForm(FieldPermissionFormMixin, forms.ModelForm): - pass diff --git a/re2o/login.py b/re2o/login.py index 83ca4d14..b867e836 100644 --- a/re2o/login.py +++ b/re2o/login.py @@ -24,7 +24,9 @@ # -*- coding: utf-8 -*- # Module d'authentification # David Sinquin, Gabriel Détraz, Goulven Kermarec - +"""re2o.login +Module in charge of handling the login process and verifications +""" import hashlib import binascii @@ -42,6 +44,7 @@ DIGEST_LEN = 20 def makeSecret(password): + """ Build a hashed and salted version of the password """ salt = os.urandom(4) h = hashlib.sha1(password.encode()) h.update(salt) @@ -49,11 +52,13 @@ def makeSecret(password): def hashNT(password): - hash = hashlib.new('md4', password.encode('utf-16le')).digest() - return binascii.hexlify(hash).upper() + """ Build a md4 hash of the password to use as the NT-password """ + hash_str = hashlib.new('md4', password.encode('utf-16le')).digest() + return binascii.hexlify(hash_str).upper() def checkPassword(challenge_password, password): + """ Check if a given password match the hash of a stored password """ challenge_bytes = decodestring(challenge_password[ALGO_LEN:].encode()) digest = challenge_bytes[:DIGEST_LEN] salt = challenge_bytes[DIGEST_LEN:] @@ -74,7 +79,7 @@ class SSHAPasswordHasher(hashers.BasePasswordHasher): algorithm = ALGO_NAME - def encode(self, password, salt, iterations=None): + def encode(self, password, salt): """ Hash and salt the given password using SSHA algorithm @@ -92,16 +97,16 @@ class SSHAPasswordHasher(hashers.BasePasswordHasher): def safe_summary(self, encoded): """ - Provides a safe summary ofthe password + Provides a safe summary of the password """ assert encoded.startswith(self.algorithm) - hash = encoded[ALGO_LEN:] - hash = binascii.hexlify(decodestring(hash.encode())).decode() + hash_str = encoded[ALGO_LEN:] + hash_str = binascii.hexlify(decodestring(hash_str.encode())).decode() return OrderedDict([ ('algorithm', self.algorithm), ('iterations', 0), - ('salt', hashers.mask_hash(hash[2*DIGEST_LEN:], show=2)), - ('hash', hashers.mask_hash(hash[:2*DIGEST_LEN])), + ('salt', hashers.mask_hash(hash_str[2*DIGEST_LEN:], show=2)), + ('hash', hashers.mask_hash(hash_str[:2*DIGEST_LEN])), ]) def harden_runtime(self, password, encoded): diff --git a/re2o/management/commands/gen_contrib.py b/re2o/management/commands/gen_contrib.py index 06aec202..6003b30f 100644 --- a/re2o/management/commands/gen_contrib.py +++ b/re2o/management/commands/gen_contrib.py @@ -24,11 +24,12 @@ Write in a python file the list of all contributors sorted by number of commits. This list is extracted from the current gitlab repository. """ -from django.core.management.base import BaseCommand, CommandError import os +from django.core.management.base import BaseCommand class Command(BaseCommand): + """ The command object for `gen_contrib` """ help = 'Update contributors list' def handle(self, *args, **options): @@ -39,6 +40,8 @@ class Command(BaseCommand): ] self.stdout.write(self.style.SUCCESS("Exportation Sucessfull")) with open("re2o/contributors.py", "w") as contrib_file: - contrib_file.write("#!/usr/bin/env python3\n") + contrib_file.write("\"\"\"re2o.contributors\n") + contrib_file.write("A list of the proud contributors to Re2o\n") + contrib_file.write("\"\"\"\n") contrib_file.write("\n") contrib_file.write("CONTRIBUTORS = " + str(contributeurs)) diff --git a/re2o/mixins.py b/re2o/mixins.py index 48102378..c13e841c 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -19,23 +19,34 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""re2o.mixins +A set of mixins used all over the project to avoid duplicating code +""" from reversion import revisions as reversion class RevMixin(object): + """ A mixin to subclass the save and delete function of a model + to enforce the versioning of the object before those actions + really happen """ def save(self, *args, **kwargs): + """ Creates a version of this object and save it to database """ if self.pk is None: reversion.set_comment("Création") return super(RevMixin, self).save(*args, **kwargs) def delete(self, *args, **kwargs): + """ Creates a version of this object and delete it from database """ reversion.set_comment("Suppresion") return super(RevMixin, self).delete(*args, **kwargs) class FormRevMixin(object): + """ A mixin to subclass the save function of a form + to enforce the versionning of the object before it is really edited """ def save(self, *args, **kwargs): + """ Create a version of this object and save it to database """ if reversion.get_comment() != "" and self.changed_data != []: reversion.set_comment( reversion.get_comment() + ",%s" @@ -66,14 +77,16 @@ class AclMixin(object): @classmethod def get_classname(cls): + """ Returns the name of the class where this mixin is used """ return str(cls.__name__).lower() @classmethod def get_modulename(cls): + """ Returns the name of the module where this mixin is used """ return str(cls.__module__).split('.')[0].lower() @classmethod - def get_instance(cls, *args, **kwargs): + def get_instance(cls, *_args, **kwargs): """Récupère une instance :param objectid: Instance id à trouver :return: Une instance de la classe évidemment""" @@ -81,7 +94,7 @@ class AclMixin(object): return cls.objects.get(pk=object_id) @classmethod - def can_create(cls, user_request, *args, **kwargs): + def can_create(cls, user_request, *_args, **_kwargs): """Verifie que l'user a les bons droits pour créer un object :param user_request: instance utilisateur qui fait la requête @@ -93,7 +106,7 @@ class AclMixin(object): u"Vous n'avez pas le droit de créer un " + cls.get_classname() ) - def can_edit(self, user_request, *args, **kwargs): + def can_edit(self, user_request, *_args, **_kwargs): """Verifie que l'user a les bons droits pour editer cette instance :param self: Instance à editer @@ -106,7 +119,7 @@ class AclMixin(object): u"Vous n'avez pas le droit d'éditer des " + self.get_classname() ) - def can_delete(self, user_request, *args, **kwargs): + def can_delete(self, user_request, *_args, **_kwargs): """Verifie que l'user a les bons droits pour delete cette instance :param self: Instance à delete @@ -120,7 +133,7 @@ class AclMixin(object): ) @classmethod - def can_view_all(cls, user_request, *args, **kwargs): + def can_view_all(cls, user_request, *_args, **_kwargs): """Vérifie qu'on peut bien afficher l'ensemble des objets, droit particulier view objet correspondant :param user_request: instance user qui fait l'edition @@ -132,7 +145,7 @@ class AclMixin(object): u"Vous n'avez pas le droit de voir des " + cls.get_classname() ) - def can_view(self, user_request, *args, **kwargs): + def can_view(self, user_request, *_args, **_kwargs): """Vérifie qu'on peut bien voir cette instance particulière avec droit view objet :param self: instance à voir diff --git a/re2o/script_utils.py b/re2o/script_utils.py index 27be81ac..e1420e6d 100644 --- a/re2o/script_utils.py +++ b/re2o/script_utils.py @@ -18,22 +18,27 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""re2o.script_utils +A set of utility scripts that can be used as standalone to interact easily +with Re2o throught the CLI +""" import os +from os.path import dirname import sys import pwd -from django.core.wsgi import get_wsgi_application +from getpass import getpass +from reversion import revisions as reversion +from django.core.wsgi import get_wsgi_application from django.core.management.base import CommandError +from django.db import transaction +from django.utils.html import strip_tags + from users.models import User -from django.utils.html import strip_tags -from reversion import revisions as reversion -from django.db import transaction -from getpass import getpass - -proj_path = "/var/www/re2o" +proj_path = dirname(dirname(__file__)) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "re2o.settings") sys.path.append(proj_path) os.chdir(proj_path) diff --git a/re2o/settings.py b/re2o/settings.py index 66149c53..3e937483 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -35,38 +35,37 @@ https://docs.djangoproject.com/en/1.8/ref/settings/ from __future__ import unicode_literals -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os from .settings_local import * +# The root directory for the project +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ - # Auth definition - PASSWORD_HASHERS = ( 're2o.login.SSHAPasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', ) - -AUTH_USER_MODEL = 'users.User' -LOGIN_URL = '/login/' -LOGIN_REDIRECT_URL = '/' - +AUTH_USER_MODEL = 'users.User' # The class to use for authentication +LOGIN_URL = '/login/' # The URL for login page +LOGIN_REDIRECT_URL = '/' # The URL for redirecting after login # Application definition - -INSTALLED_APPS = ( +DJANGO_CONTRIB_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', +) +EXTERNAL_CONTRIB_APPS = ( 'bootstrap3', + 'rest_framework', + 'reversion', +) +LOCAL_APPS = ( 'users', 'machines', 'cotisations', @@ -75,11 +74,14 @@ INSTALLED_APPS = ( 're2o', 'preferences', 'logs', - 'rest_framework', - 'reversion', - 'api' -) + OPTIONNAL_APPS - + 'api', +) +INSTALLED_APPS = ( + DJANGO_CONTRIB_APPS + + EXTERNAL_CONTRIB_APPS + + LOCAL_APPS + + OPTIONNAL_APPS +) MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', @@ -93,14 +95,17 @@ MIDDLEWARE_CLASSES = ( 'reversion.middleware.RevisionMiddleware', ) +# The root url module to define the project URLs ROOT_URLCONF = 're2o.urls' +# The templates configuration (see Django documentation) TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ - os.path.join(BASE_DIR, 'templates').replace('\\', '/'), - ], + # Use only absolute paths with '/' delimiters even on Windows + os.path.join(BASE_DIR, 'templates').replace('\\', '/'), + ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -115,57 +120,50 @@ TEMPLATES = [ }, ] +# The WSGI module to use in a server environment WSGI_APPLICATION = 're2o.wsgi.application' - # Internationalization # https://docs.djangoproject.com/en/1.8/topics/i18n/ - LANGUAGE_CODE = 'en' - +USE_I18N = True +USE_L10N = True # Proritary location search for translations # then searches in {app}/locale/ for app in INSTALLED_APPS +# Use only absolute paths with '/' delimiters even on Windows LOCALE_PATHS = [ - BASE_DIR + '/templates/locale/' # For translations outside of apps + # For translations outside of apps + os.path.join(BASE_DIR, 'templates', 'locale').replace('\\', '/') ] -TIME_ZONE = 'Europe/Paris' - -USE_I18N = True - -USE_L10N = True - +# Should use time zone ? USE_TZ = True +# Router config for database DATABASE_ROUTERS = ['ldapdb.router.Router'] - -# django-bootstrap3 config dictionnary +# django-bootstrap3 config BOOTSTRAP3 = { - 'jquery_url': '/static/js/jquery-2.2.4.min.js', - 'base_url': '/static/bootstrap/', - 'include_jquery': True, - } - + 'jquery_url': '/static/js/jquery-2.2.4.min.js', + 'base_url': '/static/bootstrap/', + 'include_jquery': True, +} BOOTSTRAP_BASE_URL = '/static/bootstrap/' +# Directories where collectstatic should look for static files +# Use only absolute paths with '/' delimiters even on Windows STATICFILES_DIRS = ( - # Put strings here, like "/home/html/static" or "C:/www/django/static". - # Always use forward slashes, even on Windows. - # Don't forget to use absolute paths, not relative paths. - os.path.join( - BASE_DIR, - 'static', - ), + os.path.join(BASE_DIR, 'static').replace('\\', '/'), ) - -MEDIA_ROOT = '/var/www/re2o/media' - -STATIC_URL = '/static/' - +# Directory where the static files serverd by the server are stored STATIC_ROOT = os.path.join(BASE_DIR, 'static_files') +# The URL to access the static files +STATIC_URL = '/static/' +# Directory where the media files serverd by the server are stored +MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace('\\', '/') +# Models to use for graphs GRAPH_MODELS = { - 'all_applications': True, - 'group_models': True, + 'all_applications': True, + 'group_models': True, } diff --git a/re2o/settings_local.example.py b/re2o/settings_local.example.py index 1418abfa..ef017c5d 100644 --- a/re2o/settings_local.example.py +++ b/re2o/settings_local.example.py @@ -19,45 +19,56 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""re2o.settings_locale.example +The example settings_locale.py file with all the available +options for a locale configuration of re2o +""" from __future__ import unicode_literals +# A secret key used by the server. SECRET_KEY = 'SUPER_SECRET_KEY' +# The password to access the project database DB_PASSWORD = 'SUPER_SECRET_DB' -# AES key for secret key encryption length must be a multiple of 16 -AES_KEY = 'THE_AES_KEY' - +# AES key for secret key encryption. +# The length must be a multiple of 16 +AES_KEY = 'A_SECRET_AES_KEY' +# Should the server run in debug mode ? # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False +# A list of admins of the services. Receive mails when an error occurs ADMINS = [('Example', 'rezo-admin@example.org')] -SERVER_EMAIL = 'no-reply@example.org' - -# Obligatoire, liste des host autorisés +# The list of hostname the server will respond to. ALLOWED_HOSTS = ['URL_SERVER'] +# The time zone the server is runned in +TIME_ZONE = 'Europe/Paris' + +# The storage systems parameters to use DATABASES = { - 'default': { + 'default': { # The DB 'ENGINE': 'db_engine', 'NAME': 'db_name_value', 'USER': 'db_user_value', 'PASSWORD': DB_PASSWORD, 'HOST': 'db_host_value', }, - 'ldap': { + 'ldap': { # The LDAP 'ENGINE': 'ldapdb.backends.ldap', 'NAME': 'ldap://ldap_host_ip/', 'USER': 'ldap_dn', - # 'TLS': True, + 'TLS': True, 'PASSWORD': 'SUPER_SECRET_LDAP', - } + } } -# Security settings, à activer une fois https en place +# Security settings for secure https +# Activate once https is correctly configured SECURE_CONTENT_TYPE_NOSNIFF = False SECURE_BROWSER_XSS_FILTER = False SESSION_COOKIE_SECURE = False @@ -66,12 +77,15 @@ CSRF_COOKIE_HTTPONLY = False X_FRAME_OPTIONS = 'DENY' SESSION_COOKIE_AGE = 60 * 60 * 3 +# The path where your organization logo is stored LOGO_PATH = "static_files/logo.png" -EMAIL_HOST = 'MY_EMAIL_HOST' -EMAIL_PORT = MY_EMAIL_PORT +# The mail configuration for Re2o to send mails +SERVER_EMAIL = 'no-reply@example.org' # The mail address to use +EMAIL_HOST = 'MY_EMAIL_HOST' # The host to use +EMAIL_PORT = MY_EMAIL_PORT # The port to use -# Reglages pour la bdd ldap +# Settings of the LDAP structure LDAP = { 'base_user_dn': 'cn=Utilisateurs,dc=example,dc=org', 'base_userservice_dn': 'ou=service-users,dc=example,dc=org', @@ -80,15 +94,16 @@ LDAP = { 'user_gid': 500, } - +# A range of UID to use. Used in linux environement UID_RANGES = { 'users': [21001, 30000], 'service-users': [20000, 21000], } -# Chaque groupe a un gid assigné, voici la place libre pour assignation +# A range of GID to use. Used in linux environement GID_RANGES = { 'posix': [501, 600], } +# Some Django apps you want to add in you local project OPTIONNAL_APPS = () diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py index cb0d5a68..fccbea1d 100644 --- a/re2o/templatetags/acl.py +++ b/re2o/templatetags/acl.py @@ -302,7 +302,7 @@ def acl_change_filter(parser, token): try: tag_content = token.split_contents() - tag_name = tag_content[0] + # tag_name = tag_content[0] model_name = tag_content[1] field_name = tag_content[2] args = tag_content[3:] diff --git a/re2o/templatetags/self_adhesion.py b/re2o/templatetags/self_adhesion.py index 9c749b52..3b463e68 100644 --- a/re2o/templatetags/self_adhesion.py +++ b/re2o/templatetags/self_adhesion.py @@ -19,13 +19,20 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""re2o.templatetags.self_adhesion +A simple templatagetag which returns the value of the option `self_adhesion` +which indicated if a user can creates an account by himself +""" + from django import template -from preferences.models import OptionalUser, GeneralOption +from preferences.models import OptionalUser + register = template.Library() @register.simple_tag def self_adhesion(): + """ Returns True if the user are allowed to create accounts """ options, _created = OptionalUser.objects.get_or_create() return options.self_adhesion diff --git a/re2o/utils.py b/re2o/utils.py index 0616bd26..5a4f9400 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -36,18 +36,13 @@ Fonction : from __future__ import unicode_literals - from django.utils import timezone from django.db.models import Q -from django.contrib import messages -from django.shortcuts import redirect -from django.urls import reverse from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from cotisations.models import Cotisation, Facture, Paiement, Vente -from machines.models import Domain, Interface, Machine +from cotisations.models import Cotisation, Facture, Vente +from machines.models import Interface, Machine from users.models import Adherent, User, Ban, Whitelist -from preferences.models import Service def all_adherent(search_time=None): diff --git a/re2o/views.py b/re2o/views.py index f1729fb4..35635992 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -26,32 +26,28 @@ les views from __future__ import unicode_literals +from itertools import chain +import git +from reversion.models import Version + from django.http import Http404 from django.urls import reverse from django.shortcuts import render, redirect from django.template.context_processors import csrf -from django.contrib.auth.decorators import login_required, permission_required -from reversion.models import Version +from django.contrib.auth.decorators import login_required from django.contrib import messages from django.conf import settings from django.utils.translation import ugettext as _ from django.views.decorators.cache import cache_page -import git -import os -import time -from itertools import chain - -from preferences.models import Service -from preferences.models import OptionalUser, GeneralOption, AssoOption -import users import preferences +from preferences.models import Service, GeneralOption, AssoOption +import users import cotisations import topologie import machines from .utils import re2o_paginator -from .settings import BASE_DIR, INSTALLED_APPS, MIDDLEWARE_CLASSES from .contributors import CONTRIBUTORS @@ -143,7 +139,7 @@ def history(request, application, object_name, object_id): """ try: model = HISTORY_BIND[application][object_name] - except KeyError as e: + except KeyError: raise Http404(u"Il n'existe pas d'historique pour ce modèle.") object_name_id = object_name + 'id' kwargs = {object_name_id: object_id} @@ -178,10 +174,13 @@ def history(request, application, object_name, object_id): @cache_page(7 * 24 * 60 * 60) def about_page(request): + """ The view for the about page. + Fetch some info about the configuration of the project. If it can't + get the info from the Git repository, fallback to default string """ option = AssoOption.objects.get() git_info_contributors = CONTRIBUTORS try: - git_repo = git.Repo(BASE_DIR) + git_repo = git.Repo(settings.BASE_DIR) git_info_remote = ", ".join(git_repo.remote().urls) git_info_branch = git_repo.active_branch.name last_commit = git_repo.commit() @@ -194,7 +193,7 @@ def about_page(request): git_info_commit = NO_GIT_MSG git_info_commit_date = NO_GIT_MSG - dependencies = INSTALLED_APPS + MIDDLEWARE_CLASSES + dependencies = settings.INSTALLED_APPS + settings.MIDDLEWARE_CLASSES return render( request, diff --git a/re2o/wsgi.py b/re2o/wsgi.py index deb6b330..2a60249b 100644 --- a/re2o/wsgi.py +++ b/re2o/wsgi.py @@ -32,8 +32,9 @@ https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ from __future__ import unicode_literals import os -import sys from os.path import dirname +import sys + from django.core.wsgi import get_wsgi_application From 595c6b99fa4c36807093fa5a41b776afa86142ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 21:44:00 +0000 Subject: [PATCH 16/22] Pylint compliance on search --- search/__init__.py | 3 +++ search/acl.py | 2 +- search/tests.py | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/search/__init__.py b/search/__init__.py index df6e4256..5460ff54 100644 --- a/search/__init__.py +++ b/search/__init__.py @@ -20,5 +20,8 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""search +The app in charge of evrything related to the search function +""" from .acl import * diff --git a/search/acl.py b/search/acl.py index f2b289bf..7ae541f8 100644 --- a/search/acl.py +++ b/search/acl.py @@ -27,7 +27,7 @@ Here are defined some functions to check acl on the application. """ -def can_view(user): +def can_view(_user): """Check if an user can view the application. Args: diff --git a/search/tests.py b/search/tests.py index 21fa6d24..d15f1f07 100644 --- a/search/tests.py +++ b/search/tests.py @@ -19,7 +19,10 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""search.tests +The tests for the Search module. +""" -from django.test import TestCase +# from django.test import TestCase # Create your tests here. From 4a1b97bbe33f70f5d89c32017b1d5e9a107b45c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 22:06:29 +0000 Subject: [PATCH 17/22] Pylint compliance on topologie --- topologie/__init__.py | 4 ++ topologie/forms.py | 11 +-- topologie/models.py | 48 +++++++------ topologie/tests.py | 5 +- topologie/views.py | 152 +++++++++++++++++++++--------------------- 5 files changed, 118 insertions(+), 102 deletions(-) diff --git a/topologie/__init__.py b/topologie/__init__.py index df6e4256..9e8202ea 100644 --- a/topologie/__init__.py +++ b/topologie/__init__.py @@ -20,5 +20,9 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""topologie +The app in charge of handling all the informations about the network +topology like the switches, the rooms, how are the connections, ... +""" from .acl import * diff --git a/topologie/forms.py b/topologie/forms.py index 013321b6..18831217 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -32,15 +32,17 @@ NewSwitchForm) from __future__ import unicode_literals +from django import forms +from django.forms import ModelForm +from django.db.models import Prefetch + from machines.models import Interface from machines.forms import ( - EditInterfaceForm, EditMachineForm, NewMachineForm ) -from django import forms -from django.forms import ModelForm, Form -from django.db.models import Prefetch +from re2o.mixins import FormRevMixin + from .models import ( Port, Switch, @@ -52,7 +54,6 @@ from .models import ( SwitchBay, Building, ) -from re2o.mixins import FormRevMixin class PortForm(FormRevMixin, ModelForm): diff --git a/topologie/models.py b/topologie/models.py index 2cb37c63..75424f04 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -47,7 +47,7 @@ from django.db import IntegrityError from django.db import transaction from reversion import revisions as reversion -from machines.models import Machine, Interface, regen +from machines.models import Machine, regen from re2o.mixins import AclMixin, RevMixin @@ -193,6 +193,7 @@ class Switch(AclMixin, Machine): ValidationError("Création d'un port existant.") def main_interface(self): + """ Returns the 'main' interface of the switch """ return self.interface_set.first() def __str__(self): @@ -332,14 +333,15 @@ class Port(AclMixin, RevMixin, models.Model): ("view_port", "Peut voir un objet port"), ) - def get_instance(portid, *args, **kwargs): - return Port.objects\ - .select_related('machine_interface__domain__extension')\ - .select_related('machine_interface__machine__switch')\ - .select_related('room')\ - .select_related('related')\ - .prefetch_related('switch__interface_set__domain__extension')\ - .get(pk=portid) + @classmethod + def get_instance(cls, portid, *_args, **kwargs): + return (cls.objects + .select_related('machine_interface__domain__extension') + .select_related('machine_interface__machine__switch') + .select_related('room') + .select_related('related') + .prefetch_related('switch__interface_set__domain__extension') + .get(pk=portid)) def make_port_related(self): """ Synchronise le port distant sur self""" @@ -364,18 +366,24 @@ class Port(AclMixin, RevMixin, models.Model): cohérence""" if hasattr(self, 'switch'): if self.port > self.switch.number: - raise ValidationError("Ce port ne peut exister,\ - numero trop élevé") - if self.room and self.machine_interface or self.room and\ - self.related or self.machine_interface and self.related: - raise ValidationError("Chambre, interface et related_port sont\ - mutuellement exclusifs") + raise ValidationError( + "Ce port ne peut exister, numero trop élevé" + ) + if (self.room and self.machine_interface or + self.room and self.related or + self.machine_interface and self.related): + raise ValidationError( + "Chambre, interface et related_port sont mutuellement " + "exclusifs" + ) if self.related == self: raise ValidationError("On ne peut relier un port à lui même") if self.related and not self.related.related: if self.related.machine_interface or self.related.room: - raise ValidationError("Le port relié est déjà occupé, veuillez\ - le libérer avant de créer une relation") + raise ValidationError( + "Le port relié est déjà occupé, veuillez le libérer " + "avant de créer une relation" + ) else: self.make_port_related() elif hasattr(self, 'related_port'): @@ -403,18 +411,18 @@ class Room(AclMixin, RevMixin, models.Model): @receiver(post_save, sender=AccessPoint) -def ap_post_save(sender, **kwargs): +def ap_post_save(_sender, **_kwargs): """Regeneration des noms des bornes vers le controleur""" regen('unifi-ap-names') @receiver(post_delete, sender=AccessPoint) -def ap_post_delete(sender, **kwargs): +def ap_post_delete(_sender, **_kwargs): """Regeneration des noms des bornes vers le controleur""" regen('unifi-ap-names') @receiver(post_delete, sender=Stack) -def stack_post_delete(sender, **kwargs): +def stack_post_delete(_sender, **_kwargs): """Vide les id des switches membres d'une stack supprimée""" Switch.objects.filter(stack=None).update(stack_member_id=None) diff --git a/topologie/tests.py b/topologie/tests.py index 21fa6d24..dfe72a14 100644 --- a/topologie/tests.py +++ b/topologie/tests.py @@ -19,7 +19,10 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""topologie.tests +The tests for the Topologie module. +""" -from django.test import TestCase +# from django.test import TestCase # Create your tests here. diff --git a/topologie/views.py b/topologie/views.py index 12bea920..132148ac 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -38,36 +38,11 @@ from __future__ import unicode_literals from django.urls import reverse from django.shortcuts import render, redirect from django.contrib import messages -from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.decorators import login_required from django.db import IntegrityError -from django.db import transaction from django.db.models import ProtectedError, Prefetch from django.core.exceptions import ValidationError -from topologie.models import ( - Switch, - Port, - Room, - Stack, - ModelSwitch, - ConstructorSwitch, - AccessPoint, - SwitchBay, - Building -) -from topologie.forms import EditPortForm, NewSwitchForm, EditSwitchForm -from topologie.forms import ( - AddPortForm, - EditRoomForm, - StackForm, - EditModelSwitchForm, - EditConstructorSwitchForm, - CreatePortsForm, - AddAccessPointForm, - EditAccessPointForm, - EditSwitchBayForm, - EditBuildingForm -) from users.views import form from re2o.utils import re2o_paginator, SortTable from re2o.acl import ( @@ -79,8 +54,6 @@ from re2o.acl import ( ) from machines.forms import ( DomainForm, - NewMachineForm, - EditMachineForm, EditInterfaceForm, AddInterfaceForm ) @@ -88,6 +61,33 @@ from machines.views import generate_ipv4_mbf_param from machines.models import Interface from preferences.models import AssoOption, GeneralOption +from .models import ( + Switch, + Port, + Room, + Stack, + ModelSwitch, + ConstructorSwitch, + AccessPoint, + SwitchBay, + Building +) +from .forms import ( + EditPortForm, + NewSwitchForm, + EditSwitchForm, + AddPortForm, + EditRoomForm, + StackForm, + EditModelSwitchForm, + EditConstructorSwitchForm, + CreatePortsForm, + AddAccessPointForm, + EditAccessPointForm, + EditSwitchBayForm, + EditBuildingForm +) + @login_required @can_view_all(Switch) @@ -296,7 +296,7 @@ def new_port(request, switchid): @login_required @can_edit(Port) -def edit_port(request, port_object, portid): +def edit_port(request, port_object, _portid): """ Edition d'un port. Permet de changer le switch parent et l'affectation du port""" @@ -322,7 +322,7 @@ def edit_port(request, port_object, portid): @login_required @can_delete(Port) -def del_port(request, port, portid): +def del_port(request, port, _portid): """ Supprime le port""" if request.method == "POST": try: @@ -358,7 +358,7 @@ def new_stack(request): @login_required @can_edit(Stack) -def edit_stack(request, stack, stackid): +def edit_stack(request, stack, _stackid): """Edition d'un stack (nombre de switches, nom...)""" stack = StackForm(request.POST or None, instance=stack) if stack.is_valid(): @@ -374,7 +374,7 @@ def edit_stack(request, stack, stackid): @login_required @can_delete(Stack) -def del_stack(request, stack, stackid): +def del_stack(request, stack, _stackid): """Supprime un stack""" if request.method == "POST": try: @@ -392,7 +392,7 @@ def del_stack(request, stack, stackid): @login_required @can_edit(Stack) -def edit_switchs_stack(request, stack, stackid): +def edit_switchs_stack(request, stack, _stackid): """Permet d'éditer la liste des switches dans une stack et l'ajouter""" if request.method == "POST": @@ -429,17 +429,17 @@ def new_switch(request): "créer ou le linker dans preferences") ) return redirect(reverse('topologie:index')) - new_switch = switch.save(commit=False) - new_switch.user = user - new_interface_instance = interface.save(commit=False) - domain.instance.interface_parent = new_interface_instance + new_switch_obj = switch.save(commit=False) + new_switch_obj.user = user + new_interface_obj = interface.save(commit=False) + domain.instance.interface_parent = new_interface_obj if domain.is_valid(): - new_domain_instance = domain.save(commit=False) - new_switch.save() - new_interface_instance.machine = new_switch - new_interface_instance.save() - new_domain_instance.interface_parent = new_interface_instance - new_domain_instance.save() + new_domain_obj = domain.save(commit=False) + new_switch_obj.save() + new_interface_obj.machine = new_switch_obj + new_interface_obj.save() + new_domain_obj.interface_parent = new_interface_obj + new_domain_obj.save() messages.success(request, "Le switch a été créé") return redirect(reverse('topologie:index')) i_mbf_param = generate_ipv4_mbf_param(interface, False) @@ -518,15 +518,15 @@ def edit_switch(request, switch, switchid): instance=switch.interface_set.first().domain ) if switch_form.is_valid() and interface_form.is_valid(): - new_switch = switch_form.save(commit=False) - new_interface_instance = interface_form.save(commit=False) - new_domain = domain_form.save(commit=False) + new_switch_obj = switch_form.save(commit=False) + new_interface_obj = interface_form.save(commit=False) + new_domain_obj = domain_form.save(commit=False) if switch_form.changed_data: - new_switch.save() + new_switch_obj.save() if interface_form.changed_data: - new_interface_instance.save() + new_interface_obj.save() if domain_form.changed_data: - new_domain.save() + new_domain_obj.save() messages.success(request, "Le switch a bien été modifié") return redirect(reverse('topologie:index')) i_mbf_param = generate_ipv4_mbf_param(interface_form, False) @@ -570,17 +570,17 @@ def new_ap(request): "créer ou le linker dans preferences") ) return redirect(reverse('topologie:index')) - new_ap = ap.save(commit=False) - new_ap.user = user - new_interface = interface.save(commit=False) - domain.instance.interface_parent = new_interface + new_ap_obj = ap.save(commit=False) + new_ap_obj.user = user + new_interface_obj = interface.save(commit=False) + domain.instance.interface_parent = new_interface_obj if domain.is_valid(): - new_domain_instance = domain.save(commit=False) - new_ap.save() - new_interface.machine = new_ap - new_interface.save() - new_domain_instance.interface_parent = new_interface - new_domain_instance.save() + new_domain_obj = domain.save(commit=False) + new_ap_obj.save() + new_interface_obj.machine = new_ap_obj + new_interface_obj.save() + new_domain_obj.interface_parent = new_interface_obj + new_domain_obj.save() messages.success(request, "La borne a été créé") return redirect(reverse('topologie:index-ap')) i_mbf_param = generate_ipv4_mbf_param(interface, False) @@ -599,7 +599,7 @@ def new_ap(request): @login_required @can_edit(AccessPoint) -def edit_ap(request, ap, accesspointid): +def edit_ap(request, ap, _accesspointid): """ Edition d'un switch. Permet de chambre nombre de ports, place dans le stack, interface et machine associée""" interface_form = EditInterfaceForm( @@ -625,15 +625,15 @@ def edit_ap(request, ap, accesspointid): "créer ou le linker dans preferences") ) return redirect(reverse('topologie:index-ap')) - new_ap = ap_form.save(commit=False) - new_interface = interface_form.save(commit=False) - new_domain = domain_form.save(commit=False) + new_ap_obj = ap_form.save(commit=False) + new_interface_obj = interface_form.save(commit=False) + new_domain_obj = domain_form.save(commit=False) if ap_form.changed_data: - new_ap.save() + new_ap_obj.save() if interface_form.changed_data: - new_interface.save() + new_interface_obj.save() if domain_form.changed_data: - new_domain.save() + new_domain_obj.save() messages.success(request, "La borne a été modifiée") return redirect(reverse('topologie:index-ap')) i_mbf_param = generate_ipv4_mbf_param(interface_form, False) @@ -668,7 +668,7 @@ def new_room(request): @login_required @can_edit(Room) -def edit_room(request, room, roomid): +def edit_room(request, room, _roomid): """ Edition numero et details de la chambre""" room = EditRoomForm(request.POST or None, instance=room) if room.is_valid(): @@ -685,7 +685,7 @@ def edit_room(request, room, roomid): @login_required @can_delete(Room) -def del_room(request, room, roomid): +def del_room(request, room, _roomid): """ Suppression d'un chambre""" if request.method == "POST": try: @@ -723,7 +723,7 @@ def new_model_switch(request): @login_required @can_edit(ModelSwitch) -def edit_model_switch(request, model_switch, modelswitchid): +def edit_model_switch(request, model_switch, _modelswitchid): """ Edition d'un modèle de switch""" model_switch = EditModelSwitchForm( @@ -744,7 +744,7 @@ def edit_model_switch(request, model_switch, modelswitchid): @login_required @can_delete(ModelSwitch) -def del_model_switch(request, model_switch, modelswitchid): +def del_model_switch(request, model_switch, _modelswitchid): """ Suppression d'un modèle de switch""" if request.method == "POST": try: @@ -782,7 +782,7 @@ def new_switch_bay(request): @login_required @can_edit(SwitchBay) -def edit_switch_bay(request, switch_bay, switchbayid): +def edit_switch_bay(request, switch_bay, _switchbayid): """ Edition d'une baie de switch""" switch_bay = EditSwitchBayForm(request.POST or None, instance=switch_bay) if switch_bay.is_valid(): @@ -799,7 +799,7 @@ def edit_switch_bay(request, switch_bay, switchbayid): @login_required @can_delete(SwitchBay) -def del_switch_bay(request, switch_bay, switchbayid): +def del_switch_bay(request, switch_bay, _switchbayid): """ Suppression d'une baie de switch""" if request.method == "POST": try: @@ -837,7 +837,7 @@ def new_building(request): @login_required @can_edit(Building) -def edit_building(request, building, buildingid): +def edit_building(request, building, _buildingid): """ Edition d'un batiment""" building = EditBuildingForm(request.POST or None, instance=building) if building.is_valid(): @@ -854,7 +854,7 @@ def edit_building(request, building, buildingid): @login_required @can_delete(Building) -def del_building(request, building, buildingid): +def del_building(request, building, _buildingid): """ Suppression d'un batiment""" if request.method == "POST": try: @@ -892,7 +892,7 @@ def new_constructor_switch(request): @login_required @can_edit(ConstructorSwitch) -def edit_constructor_switch(request, constructor_switch, constructorswitchid): +def edit_constructor_switch(request, constructor_switch, _constructorswitchid): """ Edition d'un constructeur de switch""" constructor_switch = EditConstructorSwitchForm( @@ -913,7 +913,7 @@ def edit_constructor_switch(request, constructor_switch, constructorswitchid): @login_required @can_delete(ConstructorSwitch) -def del_constructor_switch(request, constructor_switch, constructorswitchid): +def del_constructor_switch(request, constructor_switch, _constructorswitchid): """ Suppression d'un constructeur de switch""" if request.method == "POST": try: From 1469c7d6a6a09b02b54e13f4949558a540994699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sat, 14 Apr 2018 23:16:49 +0000 Subject: [PATCH 18/22] Pylint compliance on users --- users/__init__.py | 7 +++ users/admin.py | 2 +- users/forms.py | 11 ++-- users/models.py | 126 +++++++++++++++++++++++++++++++------------ users/serializers.py | 9 ++++ users/tests.py | 5 +- users/views.py | 122 +++++++++++++++++++++-------------------- 7 files changed, 180 insertions(+), 102 deletions(-) diff --git a/users/__init__.py b/users/__init__.py index df6e4256..b661a850 100644 --- a/users/__init__.py +++ b/users/__init__.py @@ -20,5 +20,12 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""users +The app managing everything related to the users such as personal +informations or the right groups. +This is probably the most central app. It is strongly linked with +all the other because a user has devices (machines), a cotisation +(cotisations), a room (topologie) +""" from .acl import * diff --git a/users/admin.py b/users/admin.py index 727b129c..a902d2e2 100644 --- a/users/admin.py +++ b/users/admin.py @@ -132,7 +132,7 @@ class UserAdmin(VersionAdmin, BaseUserAdmin): ) # Need to reset the settings from BaseUserAdmin # They are using fields we don't use like 'is_staff' - list_filter=() + list_filter = () fieldsets = ( (None, {'fields': ('pseudo', 'password')}), ( diff --git a/users/forms.py b/users/forms.py index a43db749..0a17df8b 100644 --- a/users/forms.py +++ b/users/forms.py @@ -41,6 +41,10 @@ from django.utils import timezone from django.contrib.auth.models import Group, Permission from preferences.models import OptionalUser +from re2o.utils import remove_user_room +from re2o.mixins import FormRevMixin +from re2o.field_permissions import FieldPermissionFormMixin + from .models import ( User, ServiceUser, @@ -52,9 +56,6 @@ from .models import ( Adherent, Club ) -from re2o.utils import remove_user_room -from re2o.mixins import FormRevMixin -from re2o.field_permissions import FieldPermissionFormMixin class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): @@ -97,8 +98,8 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): def clean_selfpasswd(self): """Verifie si il y a lieu que le mdp self est correct""" if not self.instance.check_password( - self.cleaned_data.get("selfpasswd") - ): + self.cleaned_data.get("selfpasswd") + ): raise forms.ValidationError("Le mot de passe actuel est incorrect") return diff --git a/users/models.py b/users/models.py index a0561aed..47203a29 100644 --- a/users/models.py +++ b/users/models.py @@ -187,6 +187,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, (2, 'STATE_ARCHIVE'), ) + # TODO : Use only one of auto_uid and get_fresh_user_uid + @staticmethod def auto_uid(): """Renvoie un uid libre""" return get_fresh_user_uid() @@ -278,10 +280,14 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, @cached_property def is_class_club(self): + """ Returns True if the object is a Club (subclassing User) """ + # TODO : change to isinstance (cleaner) return hasattr(self, 'club') @cached_property def is_class_adherent(self): + """ Returns True if the object is a Adherent (subclassing User) """ + # TODO : change to isinstance (cleaner) return hasattr(self, 'adherent') @property @@ -607,8 +613,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, une machine inconnue sur le compte de l'user""" all_interfaces = self.user_interfaces(active=False) if all_interfaces.count() > OptionalMachine.get_cached_value( - 'max_lambdauser_interfaces' - ): + 'max_lambdauser_interfaces' + ): return False, "Maximum de machines enregistrees atteinte" if not nas_type: return False, "Re2o ne sait pas à quel machinetype affecter cette\ @@ -686,8 +692,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, num += 1 return composed_pseudo(num) - def can_edit(self, user_request, *args, **kwargs): - """Check if an user can edit an user object. + def can_edit(self, user_request, *_args, **_kwargs): + """Check if a user can edit a user object. :param self: The user which is to be edited. :param user_request: The user who requests to edit self. @@ -722,7 +728,15 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, return False, (u"Vous ne pouvez éditer un autre utilisateur " "que vous même") - def can_change_password(self, user_request, *args, **kwargs): + def can_change_password(self, user_request, *_args, **_kwargs): + """Check if a user can change a user's password + + :param self: The user which is to be edited + :param user_request: The user who request to edit self + :returns: a message and a boolean which is True if self is a club + and user_request one of it's admins, or if user_request is self, + or if user_request has the right to change other's password + """ if self.is_class_club and user_request.is_class_adherent: if (self == user_request or user_request.has_perm('users.change_user_password') or @@ -743,38 +757,65 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, return False, (u"Vous ne pouvez éditer un autre utilisateur " "que vous même") - def check_selfpasswd(self, user_request, *args, **kwargs): + def check_selfpasswd(self, user_request, *_args, **_kwargs): + """ Returns (True, None) if user_request is self, else returns + (False, None) + """ return user_request == self, None @staticmethod - def can_change_state(user_request, *args, **kwargs): + def can_change_state(user_request, *_args, **_kwargs): + """ Check if a user can change a state + + :param user_request: The user who request + :returns: a message and a boolean which is True if the user has + the right to change a state + """ return ( user_request.has_perm('users.change_user_state'), "Droit requis pour changer l'état" ) @staticmethod - def can_change_shell(user_request, *args, **kwargs): + def can_change_shell(user_request, *_args, **_kwargs): + """ Check if a user can change a shell + + :param user_request: The user who request + :returns: a message and a boolean which is True if the user has + the right to change a shell + """ return ( user_request.has_perm('users.change_user_shell'), "Droit requis pour changer le shell" ) @staticmethod - def can_change_force(user_request, *args, **kwargs): + def can_change_force(user_request, *_args, **_kwargs): + """ Check if a user can change a force + + :param user_request: The user who request + :returns: a message and a boolean which is True if the user has + the right to change a force + """ return ( user_request.has_perm('users.change_user_force'), "Droit requis pour forcer le déménagement" ) @staticmethod - def can_change_groups(user_request, *args, **kwargs): + def can_change_groups(user_request, *_args, **_kwargs): + """ Check if a user can change a group + + :param user_request: The user who request + :returns: a message and a boolean which is True if the user has + the right to change a group + """ return ( user_request.has_perm('users.change_user_groups'), "Droit requis pour éditer les groupes de l'user" ) - def can_view(self, user_request, *args, **kwargs): + def can_view(self, user_request, *_args, **_kwargs): """Check if an user can view an user object. :param self: The targeted user. @@ -798,7 +839,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, return False, (u"Vous ne pouvez voir un autre utilisateur " "que vous même") - def can_view_all(user_request, *args, **kwargs): + @staticmethod + def can_view_all(user_request, *_args, **_kwargs): """Check if an user can access to the list of every user objects :param user_request: The user who wants to view the list. @@ -810,7 +852,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, u"Vous n'avez pas accès à la liste des utilisateurs." ) - def can_delete(self, user_request, *args, **kwargs): + def can_delete(self, user_request, *_args, **_kwargs): """Check if an user can delete an user object. :param self: The user who is to be deleted. @@ -836,6 +878,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, class Adherent(User): + """ A class representing a member (it's a user with special + informations) """ PRETTY_NAME = "Adhérents" name = models.CharField(max_length=255) room = models.OneToOneField( @@ -845,15 +889,17 @@ class Adherent(User): null=True ) - def get_instance(adherentid, *args, **kwargs): + @classmethod + def get_instance(cls, adherentid, *_args, **_kwargs): """Try to find an instance of `Adherent` with the given id. :param adherentid: The id of the adherent we are looking for. :return: An adherent. """ - return Adherent.objects.get(pk=adherentid) + return cls.objects.get(pk=adherentid) - def can_create(user_request, *args, **kwargs): + @staticmethod + def can_create(user_request, *_args, **_kwargs): """Check if an user can create an user object. :param user_request: The user who wants to create a user object. @@ -875,6 +921,8 @@ class Adherent(User): class Club(User): + """ A class representing a club (it is considered as a user + with special informations) """ PRETTY_NAME = "Clubs" room = models.ForeignKey( 'topologie.Room', @@ -896,7 +944,8 @@ class Club(User): default=False ) - def can_create(user_request, *args, **kwargs): + @staticmethod + def can_create(user_request, *_args, **_kwargs): """Check if an user can create an user object. :param user_request: The user who wants to create a user object. @@ -914,7 +963,8 @@ class Club(User): u"Vous n'avez pas le droit de créer un club" ) - def can_view_all(user_request, *args, **kwargs): + @staticmethod + def can_view_all(user_request, *_args, **_kwargs): """Check if an user can access to the list of every user objects :param user_request: The user who wants to view the list. @@ -930,22 +980,23 @@ class Club(User): return True, None return False, u"Vous n'avez pas accès à la liste des utilisateurs." - def get_instance(clubid, *args, **kwargs): + @classmethod + def get_instance(cls, clubid, *_args, **_kwargs): """Try to find an instance of `Club` with the given id. :param clubid: The id of the adherent we are looking for. :return: A club. """ - return Club.objects.get(pk=clubid) + return cls.objects.get(pk=clubid) @receiver(post_save, sender=Adherent) @receiver(post_save, sender=Club) @receiver(post_save, sender=User) -def user_post_save(sender, **kwargs): +def user_post_save(_sender, **kwargs): """ Synchronisation post_save : envoie le mail de bienvenue si creation Synchronise le ldap""" - is_created = kwargs['created'] + # is_created = kwargs['created'] user = kwargs['instance'] # TODO : remove if unnecessary # if is_created: @@ -962,7 +1013,7 @@ def user_post_save(sender, **kwargs): @receiver(post_delete, sender=Adherent) @receiver(post_delete, sender=Club) @receiver(post_delete, sender=User) -def user_post_delete(sender, **kwargs): +def user_post_delete(_sender, **kwargs): """Post delete d'un user, on supprime son instance ldap""" user = kwargs['instance'] user.ldap_del() @@ -1005,6 +1056,14 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): ("view_serviceuser", "Peut voir un objet serviceuser"), ) + def get_full_name(self): + """ Renvoie le nom complet du serviceUser formaté nom/prénom""" + return "ServiceUser <{name}>".format(name=self.pseudo) + + def get_short_name(self): + """ Renvoie seulement le nom""" + return self.pseudo + def ldap_sync(self): """ Synchronisation du ServiceUser dans sa version ldap""" try: @@ -1041,14 +1100,14 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): @receiver(post_save, sender=ServiceUser) -def service_user_post_save(sender, **kwargs): +def service_user_post_save(_sender, **kwargs): """ Synchronise un service user ldap après modification django""" service_user = kwargs['instance'] service_user.ldap_sync() @receiver(post_delete, sender=ServiceUser) -def service_user_post_delete(sender, **kwargs): +def service_user_post_delete(_sender, **kwargs): """ Supprime un service user ldap après suppression django""" service_user = kwargs['instance'] service_user.ldap_del() @@ -1123,14 +1182,14 @@ class ListRight(RevMixin, AclMixin, Group): @receiver(post_save, sender=ListRight) -def listright_post_save(sender, **kwargs): +def listright_post_save(_sender, **kwargs): """ Synchronise le droit ldap quand il est modifié""" right = kwargs['instance'] right.ldap_sync() @receiver(post_delete, sender=ListRight) -def listright_post_delete(sender, **kwargs): +def listright_post_delete(_sender, **kwargs): """Suppression d'un groupe ldap après suppression coté django""" right = kwargs['instance'] right.ldap_del() @@ -1203,7 +1262,7 @@ class Ban(RevMixin, AclMixin, models.Model): """Ce ban est-il actif?""" return self.date_end > timezone.now() - def can_view(self, user_request, *args, **kwargs): + def can_view(self, user_request, *_args, **_kwargs): """Check if an user can view a Ban object. :param self: The targeted object. @@ -1223,7 +1282,7 @@ class Ban(RevMixin, AclMixin, models.Model): @receiver(post_save, sender=Ban) -def ban_post_save(sender, **kwargs): +def ban_post_save(_sender, **kwargs): """ Regeneration de tous les services après modification d'un ban""" ban = kwargs['instance'] is_created = kwargs['created'] @@ -1240,7 +1299,7 @@ def ban_post_save(sender, **kwargs): @receiver(post_delete, sender=Ban) -def ban_post_delete(sender, **kwargs): +def ban_post_delete(_sender, **kwargs): """ Regen de tous les services après suppression d'un ban""" user = kwargs['instance'].user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) @@ -1266,9 +1325,10 @@ class Whitelist(RevMixin, AclMixin, models.Model): ) def is_active(self): + """ Is this whitelisting active ? """ return self.date_end > timezone.now() - def can_view(self, user_request, *args, **kwargs): + def can_view(self, user_request, *_args, **_kwargs): """Check if an user can view a Whitelist object. :param self: The targeted object. @@ -1288,7 +1348,7 @@ class Whitelist(RevMixin, AclMixin, models.Model): @receiver(post_save, sender=Whitelist) -def whitelist_post_save(sender, **kwargs): +def whitelist_post_save(_sender, **kwargs): """Après modification d'une whitelist, on synchronise les services et on lui permet d'avoir internet""" whitelist = kwargs['instance'] @@ -1305,7 +1365,7 @@ def whitelist_post_save(sender, **kwargs): @receiver(post_delete, sender=Whitelist) -def whitelist_post_delete(sender, **kwargs): +def whitelist_post_delete(_sender, **kwargs): """Après suppression d'une whitelist, on supprime l'accès internet en forçant la régénration""" user = kwargs['instance'].user diff --git a/users/serializers.py b/users/serializers.py index d2f66abe..be925881 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -22,11 +22,17 @@ # Maël Kervella +"""users.serializers +Serializers for the User app +""" + from rest_framework import serializers from users.models import Club, Adherent class MailingSerializer(serializers.ModelSerializer): + """ Serializer to build Mailing objects """ + name = serializers.CharField(source='pseudo') class Meta: @@ -35,6 +41,9 @@ class MailingSerializer(serializers.ModelSerializer): class MailingMemberSerializer(serializers.ModelSerializer): + """ Serializer fot the Adherent objects (who belong to a + Mailing) """ + class Meta: model = Adherent fields = ('email',) diff --git a/users/tests.py b/users/tests.py index 21fa6d24..85a8e9f1 100644 --- a/users/tests.py +++ b/users/tests.py @@ -19,7 +19,10 @@ # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""users.tests +The tests for the Users module. +""" -from django.test import TestCase +# from django.test import TestCase # Create your tests here. diff --git a/users/views.py b/users/views.py index d82d5563..a7abf653 100644 --- a/users/views.py +++ b/users/views.py @@ -39,22 +39,37 @@ from django.urls import reverse from django.shortcuts import get_object_or_404, render, redirect from django.contrib import messages from django.contrib.auth.decorators import login_required, permission_required -from django.db.models import ProtectedError, Q -from django.db import IntegrityError +from django.db.models import ProtectedError from django.utils import timezone from django.db import transaction from django.http import HttpResponse from django.http import HttpResponseRedirect from django.views.decorators.csrf import csrf_exempt - from rest_framework.renderers import JSONRenderer - - -from reversion.models import Version from reversion import revisions as reversion -from users.serializers import MailingSerializer, MailingMemberSerializer -from users.models import ( + +from cotisations.models import Facture +from machines.models import Machine +from preferences.models import OptionalUser, GeneralOption, AssoOption +from re2o.views import form +from re2o.utils import ( + all_has_access, + SortTable, + re2o_paginator +) +from re2o.acl import ( + can_create, + can_edit, + can_delete_set, + can_delete, + can_view, + can_view_all, + can_change +) + +from .serializers import MailingSerializer, MailingMemberSerializer +from .models import ( User, Ban, Whitelist, @@ -66,7 +81,7 @@ from users.models import ( Club, ListShell, ) -from users.forms import ( +from .forms import ( BanForm, WhitelistForm, DelSchoolForm, @@ -86,25 +101,6 @@ from users.forms import ( ClubAdminandMembersForm, GroupForm ) -from cotisations.models import Facture -from machines.models import Machine -from preferences.models import OptionalUser, GeneralOption, AssoOption - -from re2o.views import form -from re2o.utils import ( - all_has_access, - SortTable, - re2o_paginator -) -from re2o.acl import ( - can_create, - can_edit, - can_delete_set, - can_delete, - can_view, - can_view_all, - can_change -) @can_create(Adherent) @@ -162,7 +158,7 @@ def new_club(request): @login_required @can_edit(Club) -def edit_club_admin_members(request, club_instance, clubid): +def edit_club_admin_members(request, club_instance, _clubid): """Vue d'edition de la liste des users administrateurs et membres d'un club""" club = ClubAdminandMembersForm( @@ -195,27 +191,27 @@ def edit_info(request, user, userid): si l'id est différent de request.user, vérifie la possession du droit cableur """ if user.is_class_adherent: - user = AdherentForm( + user_form = AdherentForm( request.POST or None, instance=user.adherent, user=request.user ) - elif user.is_class_club: - user = ClubForm( + else: + user_form = ClubForm( request.POST or None, instance=user.club, user=request.user ) - if user.is_valid(): - if user.changed_data: - user.save() + if user_form.is_valid(): + if user_form.changed_data: + user_form.save() messages.success(request, "L'user a bien été modifié") return redirect(reverse( 'users:profil', kwargs={'userid': str(userid)} )) return form( - {'userform': user, 'action_name': "Editer l'utilisateur"}, + {'userform': user_form, 'action_name': "Editer l'utilisateur"}, 'users/user.html', request ) @@ -226,21 +222,21 @@ def edit_info(request, user, userid): def state(request, user, userid): """ Changer l'etat actif/desactivé/archivé d'un user, need droit bureau """ - state = StateForm(request.POST or None, instance=user) - if state.is_valid(): - if state.changed_data: - if state.cleaned_data['state'] == User.STATE_ARCHIVE: + state_form = StateForm(request.POST or None, instance=user) + if state_form.is_valid(): + if state_form.changed_data: + if state_form.cleaned_data['state'] == User.STATE_ARCHIVE: user.archive() - elif state.cleaned_data['state'] == User.STATE_ACTIVE: + elif state_form.cleaned_data['state'] == User.STATE_ACTIVE: user.unarchive() - state.save() + state_form.save() messages.success(request, "Etat changé avec succès") return redirect(reverse( 'users:profil', kwargs={'userid': str(userid)} )) return form( - {'userform': state, 'action_name': "Editer l'état"}, + {'userform': state_form, 'action_name': "Editer l'état"}, 'users/user.html', request ) @@ -249,17 +245,18 @@ def state(request, user, userid): @login_required @can_edit(User, 'groups') def groups(request, user, userid): - group = GroupForm(request.POST or None, instance=user) - if group.is_valid(): - if group.changed_data: - group.save() + """ View to edit the groups of a user """ + group_form = GroupForm(request.POST or None, instance=user) + if group_form.is_valid(): + if group_form.changed_data: + group_form.save() messages.success(request, "Groupes changés avec succès") return redirect(reverse( 'users:profil', kwargs={'userid': str(userid)} )) return form( - {'userform': group, 'action_name': 'Editer les groupes'}, + {'userform': group_form, 'action_name': 'Editer les groupes'}, 'users/user.html', request ) @@ -278,7 +275,7 @@ def password(request, user, userid): messages.success(request, "Le mot de passe a changé") return redirect(reverse( 'users:profil', - kwargs={'userid': str(user.id)} + kwargs={'userid': str(userid)} )) return form( {'userform': u_form, 'action_name': 'Changer le mot de passe'}, @@ -289,7 +286,8 @@ def password(request, user, userid): @login_required @can_edit(User, 'groups') -def del_group(request, user, userid, listrightid): +def del_group(request, user, _userid, listrightid): + """ View used to delete a group """ user.groups.remove(ListRight.objects.get(id=listrightid)) user.save() messages.success(request, "Droit supprimé à %s" % user) @@ -319,7 +317,7 @@ def new_serviceuser(request): @login_required @can_edit(ServiceUser) -def edit_serviceuser(request, serviceuser, serviceuserid): +def edit_serviceuser(request, serviceuser, _serviceuserid): """ Edit a ServiceUser """ serviceuser = EditServiceUserForm( request.POST or None, @@ -342,7 +340,7 @@ def edit_serviceuser(request, serviceuser, serviceuserid): @login_required @can_delete(ServiceUser) -def del_serviceuser(request, serviceuser, serviceuserid): +def del_serviceuser(request, serviceuser, _serviceuserid): """Suppression d'un ou plusieurs serviceusers""" if request.method == "POST": serviceuser.delete() @@ -365,7 +363,7 @@ def add_ban(request, user, userid): ban_instance = Ban(user=user) ban = BanForm(request.POST or None, instance=ban_instance) if ban.is_valid(): - _ban_object = ban.save() + ban.save() messages.success(request, "Bannissement ajouté") return redirect(reverse( 'users:profil', @@ -385,7 +383,7 @@ def add_ban(request, user, userid): @login_required @can_edit(Ban) -def edit_ban(request, ban_instance, banid): +def edit_ban(request, ban_instance, _banid): """ Editer un bannissement, nécessite au moins le droit bofh (a fortiori bureau) Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement""" @@ -436,7 +434,7 @@ def add_whitelist(request, user, userid): @login_required @can_edit(Whitelist) -def edit_whitelist(request, whitelist_instance, whitelistid): +def edit_whitelist(request, whitelist_instance, _whitelistid): """ Editer un accès gracieux, temporaire ou permanent. Need droit cableur Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement, @@ -476,7 +474,7 @@ def add_school(request): @login_required @can_edit(School) -def edit_school(request, school_instance, schoolid): +def edit_school(request, school_instance, _schoolid): """ Editer un établissement d'enseignement à partir du schoolid dans la base de donnée, need cableur""" school = SchoolForm(request.POST or None, instance=school_instance) @@ -537,7 +535,7 @@ def add_shell(request): @login_required @can_edit(ListShell) -def edit_shell(request, shell_instance, listshellid): +def edit_shell(request, shell_instance, _listshellid): """ Editer un shell à partir du listshellid""" shell = ShellForm(request.POST or None, instance=shell_instance) if shell.is_valid(): @@ -554,7 +552,7 @@ def edit_shell(request, shell_instance, listshellid): @login_required @can_delete(ListShell) -def del_shell(request, shell, listshellid): +def del_shell(request, shell, _listshellid): """Destruction d'un shell""" if request.method == "POST": shell.delete() @@ -586,7 +584,7 @@ def add_listright(request): @login_required @can_edit(ListRight) -def edit_listright(request, listright_instance, listrightid): +def edit_listright(request, listright_instance, _listrightid): """ Editer un groupe/droit, necessite droit bureau, à partir du listright id """ listright = ListRightForm( @@ -800,7 +798,7 @@ def mon_profil(request): @login_required @can_view(User) -def profil(request, users, userid): +def profil(request, users, _userid): """ Affiche un profil, self or cableur, prend un userid en argument """ machines = Machine.objects.filter(user=users).select_related('user')\ .prefetch_related('interface_set__domain__extension')\ @@ -924,7 +922,7 @@ class JSONResponse(HttpResponse): @csrf_exempt @login_required @permission_required('machines.serveur') -def ml_std_list(request): +def ml_std_list(_request): """ API view sending all the available standard mailings""" return JSONResponse([ {'name': 'adherents'} @@ -950,7 +948,7 @@ def ml_std_members(request, ml_name): @csrf_exempt @login_required @permission_required('machines.serveur') -def ml_club_list(request): +def ml_club_list(_request): """ API view sending all the available club mailings""" clubs = Club.objects.filter(mailing=True).values('pseudo') seria = MailingSerializer(clubs, many=True) From 0065e2e0f5dcfa611af776b909baf27c1fa86357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sun, 15 Apr 2018 01:00:05 +0000 Subject: [PATCH 19/22] Fix unused variables in kwargs --- cotisations/models.py | 12 +++++------ cotisations/views.py | 14 ++++++------- machines/models.py | 40 ++++++++++++++++++------------------- machines/views.py | 46 +++++++++++++++++++++---------------------- preferences/models.py | 10 +++++----- preferences/views.py | 2 +- topologie/models.py | 6 +++--- topologie/views.py | 32 +++++++++++++++--------------- users/models.py | 20 +++++++++---------- users/views.py | 22 ++++++++++----------- 10 files changed, 102 insertions(+), 102 deletions(-) diff --git a/cotisations/models.py b/cotisations/models.py index fa3156f0..c4c6d4af 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -224,7 +224,7 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): @receiver(post_save, sender=Facture) -def facture_post_save(_sender, **kwargs): +def facture_post_save(**kwargs): """ Synchronise the LDAP user after an invoice has been saved. """ @@ -234,7 +234,7 @@ def facture_post_save(_sender, **kwargs): @receiver(post_delete, sender=Facture) -def facture_post_delete(_sender, **kwargs): +def facture_post_delete(**kwargs): """ Synchronise the LDAP user after an invoice has been deleted. """ @@ -416,7 +416,7 @@ class Vente(RevMixin, AclMixin, models.Model): # TODO : change vente to purchase @receiver(post_save, sender=Vente) -def vente_post_save(_sender, **kwargs): +def vente_post_save(**kwargs): """ Creates a 'cotisation' related object if needed and synchronise the LDAP user when a purchase has been saved. @@ -434,7 +434,7 @@ def vente_post_save(_sender, **kwargs): # TODO : change vente to purchase @receiver(post_delete, sender=Vente) -def vente_post_delete(_sender, **kwargs): +def vente_post_delete(**kwargs): """ Synchronise the LDAP user after a purchase has been deleted. """ @@ -683,7 +683,7 @@ class Cotisation(RevMixin, AclMixin, models.Model): @receiver(post_save, sender=Cotisation) -def cotisation_post_save(_sender, **_kwargs): +def cotisation_post_save(**_kwargs): """ Mark some services as needing a regeneration after the edition of a cotisation. Indeed the membership status may have changed. @@ -695,7 +695,7 @@ def cotisation_post_save(_sender, **_kwargs): @receiver(post_delete, sender=Cotisation) -def cotisation_post_delete(_sender, **_kwargs): +def cotisation_post_delete(**_kwargs): """ Mark some services as needing a regeneration after the deletion of a cotisation. Indeed the membership status may have changed. diff --git a/cotisations/views.py b/cotisations/views.py index acf8c404..5687a0fa 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -263,7 +263,7 @@ def new_facture_pdf(request): # TODO : change facture to invoice @login_required @can_view(Facture) -def facture_pdf(request, facture, _factureid): +def facture_pdf(request, facture, **_kwargs): """ View used to generate a PDF file from an existing invoice in database Creates a line for each Purchase (thus article sold) and generate the @@ -306,7 +306,7 @@ def facture_pdf(request, facture, _factureid): # TODO : change facture to invoice @login_required @can_edit(Facture) -def edit_facture(request, facture, _factureid): +def edit_facture(request, facture, **_kwargs): """ View used to edit an existing invoice. Articles can be added or remove to the invoice and quantity @@ -347,7 +347,7 @@ def edit_facture(request, facture, _factureid): # TODO : change facture to invoice @login_required @can_delete(Facture) -def del_facture(request, facture, _factureid): +def del_facture(request, facture, **_kwargs): """ View used to delete an existing invocie. """ @@ -368,7 +368,7 @@ def del_facture(request, facture, _factureid): @login_required @can_create(Facture) @can_edit(User) -def credit_solde(request, user, _userid): +def credit_solde(request, user, **_kwargs): """ View used to edit the balance of a user. Can be use either to increase or decrease a user's balance. @@ -425,7 +425,7 @@ def add_article(request): @login_required @can_edit(Article) -def edit_article(request, article_instance, _articleid): +def edit_article(request, article_instance, **_kwargs): """ View used to edit an article. """ @@ -489,7 +489,7 @@ def add_paiement(request): # TODO : chnage paiement to Payment @login_required @can_edit(Paiement) -def edit_paiement(request, paiement_instance, _paiementid): +def edit_paiement(request, paiement_instance, *_kwargs): """ View used to edit a payment method. """ @@ -567,7 +567,7 @@ def add_banque(request): # TODO : change banque to bank @login_required @can_edit(Banque) -def edit_banque(request, banque_instance, _bbanqueid): +def edit_banque(request, banque_instance, **_kwargs): """ View used to edit a bank. """ diff --git a/machines/models.py b/machines/models.py index 927ee4d0..4282d1f5 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1519,7 +1519,7 @@ class OuverturePort(RevMixin, AclMixin, models.Model): @receiver(post_save, sender=Machine) -def machine_post_save(_sender, **kwargs): +def machine_post_save(**kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la modification d'une machine""" user = kwargs['instance'].user @@ -1529,7 +1529,7 @@ def machine_post_save(_sender, **kwargs): @receiver(post_delete, sender=Machine) -def machine_post_delete(_sender, **kwargs): +def machine_post_delete(**kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la suppression d'une machine""" machine = kwargs['instance'] @@ -1540,7 +1540,7 @@ def machine_post_delete(_sender, **kwargs): @receiver(post_save, sender=Interface) -def interface_post_save(_sender, **kwargs): +def interface_post_save(**kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la modification d'une interface""" interface = kwargs['instance'] @@ -1553,7 +1553,7 @@ def interface_post_save(_sender, **kwargs): @receiver(post_delete, sender=Interface) -def interface_post_delete(_sender, **kwargs): +def interface_post_delete(**kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la suppression d'une interface""" interface = kwargs['instance'] @@ -1562,7 +1562,7 @@ def interface_post_delete(_sender, **kwargs): @receiver(post_save, sender=IpType) -def iptype_post_save(_sender, **kwargs): +def iptype_post_save(**kwargs): """Generation des objets ip après modification d'un range ip""" iptype = kwargs['instance'] iptype.gen_ip_range() @@ -1570,7 +1570,7 @@ def iptype_post_save(_sender, **kwargs): @receiver(post_save, sender=MachineType) -def machinetype_post_save(_sender, **kwargs): +def machinetype_post_save(**kwargs): """Mise à jour des interfaces lorsque changement d'attribution d'une machinetype (changement iptype parent)""" machinetype = kwargs['instance'] @@ -1579,84 +1579,84 @@ def machinetype_post_save(_sender, **kwargs): @receiver(post_save, sender=Domain) -def domain_post_save(_sender, **_kwargs): +def domain_post_save(**_kwargs): """Regeneration dns après modification d'un domain object""" regen('dns') @receiver(post_delete, sender=Domain) -def domain_post_delete(_sender, **_kwargs): +def domain_post_delete(**_kwargs): """Regeneration dns après suppression d'un domain object""" regen('dns') @receiver(post_save, sender=Extension) -def extension_post_save(_sender, **_kwargs): +def extension_post_save(**_kwargs): """Regeneration dns après modification d'une extension""" regen('dns') @receiver(post_delete, sender=Extension) -def extension_post_selete(_sender, **_kwargs): +def extension_post_selete(**_kwargs): """Regeneration dns après suppression d'une extension""" regen('dns') @receiver(post_save, sender=SOA) -def soa_post_save(_sender, **_kwargs): +def soa_post_save(**_kwargs): """Regeneration dns après modification d'un SOA""" regen('dns') @receiver(post_delete, sender=SOA) -def soa_post_delete(_sender, **_kwargs): +def soa_post_delete(**_kwargs): """Regeneration dns après suppresson d'un SOA""" regen('dns') @receiver(post_save, sender=Mx) -def mx_post_save(_sender, **_kwargs): +def mx_post_save(**_kwargs): """Regeneration dns après modification d'un MX""" regen('dns') @receiver(post_delete, sender=Mx) -def mx_post_delete(_sender, **_kwargs): +def mx_post_delete(**_kwargs): """Regeneration dns après suppresson d'un MX""" regen('dns') @receiver(post_save, sender=Ns) -def ns_post_save(_sender, **_kwargs): +def ns_post_save(**_kwargs): """Regeneration dns après modification d'un NS""" regen('dns') @receiver(post_delete, sender=Ns) -def ns_post_delete(_sender, **_kwargs): +def ns_post_delete(**_kwargs): """Regeneration dns après modification d'un NS""" regen('dns') @receiver(post_save, sender=Txt) -def text_post_save(_sender, **_kwargs): +def text_post_save(**_kwargs): """Regeneration dns après modification d'un TXT""" regen('dns') @receiver(post_delete, sender=Txt) -def text_post_delete(_sender, **_kwargs): +def text_post_delete(**_kwargs): """Regeneration dns après modification d'un TX""" regen('dns') @receiver(post_save, sender=Srv) -def srv_post_save(_sender, **_kwargs): +def srv_post_save(**_kwargs): """Regeneration dns après modification d'un SRV""" regen('dns') @receiver(post_delete, sender=Srv) -def srv_post_delete(_sender, **_kwargs): +def srv_post_delete(**_kwargs): """Regeneration dns après modification d'un SRV""" regen('dns') diff --git a/machines/views.py b/machines/views.py index d3f8f2a7..c3033049 100644 --- a/machines/views.py +++ b/machines/views.py @@ -220,7 +220,7 @@ def generate_ipv4_mbf_param(form_obj, is_type_tt): @login_required @can_create(Machine) @can_edit(User) -def new_machine(request, user, _userid): +def new_machine(request, user, **_kwargs): """ Fonction de creation d'une machine. Cree l'objet machine, le sous objet interface et l'objet domain à partir de model forms. Trop complexe, devrait être simplifié""" @@ -264,7 +264,7 @@ def new_machine(request, user, _userid): @login_required @can_edit(Interface) -def edit_interface(request, interface_instance, _interfaceid): +def edit_interface(request, interface_instance, **_kwargs): """ Edition d'une interface. Distingue suivant les droits les valeurs de interfaces et machines que l'user peut modifier infra permet de modifier le propriétaire""" @@ -316,7 +316,7 @@ def edit_interface(request, interface_instance, _interfaceid): @login_required @can_delete(Machine) -def del_machine(request, machine, _machineid): +def del_machine(request, machine, **_kwargs): """ Supprime une machine, interfaces en mode cascade""" if request.method == "POST": machine.delete() @@ -335,7 +335,7 @@ def del_machine(request, machine, _machineid): @login_required @can_create(Interface) @can_edit(Machine) -def new_interface(request, machine, _machineid): +def new_interface(request, machine, **_kwargs): """ Ajoute une interface et son domain associé à une machine existante""" interface_form = AddInterfaceForm(request.POST or None, user=request.user) @@ -369,7 +369,7 @@ def new_interface(request, machine, _machineid): @login_required @can_delete(Interface) -def del_interface(request, interface, _interfaceid): +def del_interface(request, interface, **_kwargs): """ Supprime une interface. Domain objet en mode cascade""" if request.method == "POST": machine = interface.machine @@ -391,7 +391,7 @@ def del_interface(request, interface, _interfaceid): @login_required @can_create(Ipv6List) @can_edit(Interface) -def new_ipv6list(request, interface, _interfaceid): +def new_ipv6list(request, interface, **_kwargs): """Nouvelle ipv6""" ipv6list_instance = Ipv6List(interface=interface) ipv6 = Ipv6ListForm( @@ -415,7 +415,7 @@ def new_ipv6list(request, interface, _interfaceid): @login_required @can_edit(Ipv6List) -def edit_ipv6list(request, ipv6list_instance, _ipv6listid): +def edit_ipv6list(request, ipv6list_instance, **_kwargs): """Edition d'une ipv6""" ipv6 = Ipv6ListForm( request.POST or None, @@ -439,7 +439,7 @@ def edit_ipv6list(request, ipv6list_instance, _ipv6listid): @login_required @can_delete(Ipv6List) -def del_ipv6list(request, ipv6list, _ipv6listid): +def del_ipv6list(request, ipv6list, **_kwargs): """ Supprime une ipv6""" if request.method == "POST": interfaceid = ipv6list.interface.id @@ -476,7 +476,7 @@ def add_iptype(request): @login_required @can_edit(IpType) -def edit_iptype(request, iptype_instance, _iptypeid): +def edit_iptype(request, iptype_instance, **_kwargs): """ Edition d'un range. Ne permet pas de le redimensionner pour éviter l'incohérence""" @@ -536,7 +536,7 @@ def add_machinetype(request): @login_required @can_edit(MachineType) -def edit_machinetype(request, machinetype_instance, _machinetypeid): +def edit_machinetype(request, machinetype_instance, **_kwargs): """ View used to edit a MachineType object """ machinetype = MachineTypeForm( request.POST or None, @@ -598,7 +598,7 @@ def add_extension(request): @login_required @can_edit(Extension) -def edit_extension(request, extension_instance, _extensionid): +def edit_extension(request, extension_instance, **_kwargs): """ View used to edit an Extension object """ extension = ExtensionForm( request.POST or None, @@ -660,7 +660,7 @@ def add_soa(request): @login_required @can_edit(SOA) -def edit_soa(request, soa_instance, _soaid): +def edit_soa(request, soa_instance, **_kwargs): """ View used to edit a SOA object """ soa = SOAForm(request.POST or None, instance=soa_instance) if soa.is_valid(): @@ -718,7 +718,7 @@ def add_mx(request): @login_required @can_edit(Mx) -def edit_mx(request, mx_instance, _mxid): +def edit_mx(request, mx_instance, **_kwargs): """ View used to edit a MX object """ mx = MxForm(request.POST or None, instance=mx_instance) if mx.is_valid(): @@ -776,7 +776,7 @@ def add_ns(request): @login_required @can_edit(Ns) -def edit_ns(request, ns_instance, _nsid): +def edit_ns(request, ns_instance, **_kwargs): """ View used to edit a NS object """ ns = NsForm(request.POST or None, instance=ns_instance) if ns.is_valid(): @@ -834,7 +834,7 @@ def add_txt(request): @login_required @can_edit(Txt) -def edit_txt(request, txt_instance, _txtid): +def edit_txt(request, txt_instance, **_kwargs): """ View used to edit a TXT object """ txt = TxtForm(request.POST or None, instance=txt_instance) if txt.is_valid(): @@ -892,7 +892,7 @@ def add_srv(request): @login_required @can_edit(Srv) -def edit_srv(request, srv_instance, _srvid): +def edit_srv(request, srv_instance, **_kwargs): """ View used to edit a SRV object """ srv = SrvForm(request.POST or None, instance=srv_instance) if srv.is_valid(): @@ -956,7 +956,7 @@ def add_alias(request, interface, interfaceid): @login_required @can_edit(Domain) -def edit_alias(request, domain_instance, _domainid): +def edit_alias(request, domain_instance, **_kwargs): """ View used to edit an Alias object """ alias = AliasForm( request.POST or None, @@ -1029,7 +1029,7 @@ def add_service(request): @login_required @can_edit(Service) -def edit_service(request, service_instance, _serviceid): +def edit_service(request, service_instance, **_kwargs): """ View used to edit a Service object """ service = ServiceForm(request.POST or None, instance=service_instance) if service.is_valid(): @@ -1087,7 +1087,7 @@ def add_vlan(request): @login_required @can_edit(Vlan) -def edit_vlan(request, vlan_instance, _vlanid): +def edit_vlan(request, vlan_instance, **_kwargs): """ View used to edit a VLAN object """ vlan = VlanForm(request.POST or None, instance=vlan_instance) if vlan.is_valid(): @@ -1145,7 +1145,7 @@ def add_nas(request): @login_required @can_edit(Nas) -def edit_nas(request, nas_instance, _nasid): +def edit_nas(request, nas_instance, **_kwargs): """ View used to edit a NAS object """ nas = NasForm(request.POST or None, instance=nas_instance) if nas.is_valid(): @@ -1378,7 +1378,7 @@ def index_portlist(request): @login_required @can_edit(OuverturePortList) -def edit_portlist(request, ouvertureportlist_instance, _ouvertureportlistid): +def edit_portlist(request, ouvertureportlist_instance, **_kwargs): """ View used to edit a port policy """ port_list = EditOuverturePortListForm( request.POST or None, @@ -1417,7 +1417,7 @@ def edit_portlist(request, ouvertureportlist_instance, _ouvertureportlistid): @login_required @can_delete(OuverturePortList) -def del_portlist(request, port_list_instance, _ouvertureportlistid): +def del_portlist(request, port_list_instance, **_kwargs): """ View used to delete a port policy """ port_list_instance.delete() messages.success(request, "La liste de ports a été supprimée") @@ -1457,7 +1457,7 @@ def add_portlist(request): @login_required @can_create(OuverturePort) @can_edit(Interface) -def configure_ports(request, interface_instance, _interfaceid): +def configure_ports(request, interface_instance, **_kwargs): """ View to display the list of configured port policy for an interface """ if not interface_instance.may_have_port_open(): diff --git a/preferences/models.py b/preferences/models.py index 4495f629..e64d49ab 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -118,7 +118,7 @@ class OptionalUser(AclMixin, PreferencesModel): @receiver(post_save, sender=OptionalUser) -def optionaluser_post_save(_sender, **kwargs): +def optionaluser_post_save(**kwargs): """Ecriture dans le cache""" user_pref = kwargs['instance'] user_pref.set_in_cache() @@ -163,7 +163,7 @@ class OptionalMachine(AclMixin, PreferencesModel): @receiver(post_save, sender=OptionalMachine) -def optionalmachine_post_save(_sender, **kwargs): +def optionalmachine_post_save(**kwargs): """Synchronisation ipv6 et ecriture dans le cache""" machine_pref = kwargs['instance'] machine_pref.set_in_cache() @@ -211,7 +211,7 @@ class OptionalTopologie(AclMixin, PreferencesModel): @receiver(post_save, sender=OptionalTopologie) -def optionaltopologie_post_save(_sender, **kwargs): +def optionaltopologie_post_save(**kwargs): """Ecriture dans le cache""" topologie_pref = kwargs['instance'] topologie_pref.set_in_cache() @@ -251,7 +251,7 @@ class GeneralOption(AclMixin, PreferencesModel): @receiver(post_save, sender=GeneralOption) -def generaloption_post_save(_sender, **kwargs): +def generaloption_post_save(**kwargs): """Ecriture dans le cache""" general_pref = kwargs['instance'] general_pref.set_in_cache() @@ -325,7 +325,7 @@ class AssoOption(AclMixin, PreferencesModel): @receiver(post_save, sender=AssoOption) -def assooption_post_save(_sender, **kwargs): +def assooption_post_save(**kwargs): """Ecriture dans le cache""" asso_pref = kwargs['instance'] asso_pref.set_in_cache() diff --git a/preferences/views.py b/preferences/views.py index d7971b98..f7838281 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -136,7 +136,7 @@ def add_service(request): @login_required @can_edit(Service) -def edit_service(request, service_instance, _serviceid): +def edit_service(request, service_instance, **_kwargs): """Edition des services affichés sur la page d'accueil""" service = ServiceForm(request.POST or None, instance=service_instance) if service.is_valid(): diff --git a/topologie/models.py b/topologie/models.py index 75424f04..d8ff2d71 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -411,18 +411,18 @@ class Room(AclMixin, RevMixin, models.Model): @receiver(post_save, sender=AccessPoint) -def ap_post_save(_sender, **_kwargs): +def ap_post_save(**_kwargs): """Regeneration des noms des bornes vers le controleur""" regen('unifi-ap-names') @receiver(post_delete, sender=AccessPoint) -def ap_post_delete(_sender, **_kwargs): +def ap_post_delete(**_kwargs): """Regeneration des noms des bornes vers le controleur""" regen('unifi-ap-names') @receiver(post_delete, sender=Stack) -def stack_post_delete(_sender, **_kwargs): +def stack_post_delete(**_kwargs): """Vide les id des switches membres d'une stack supprimée""" Switch.objects.filter(stack=None).update(stack_member_id=None) diff --git a/topologie/views.py b/topologie/views.py index 132148ac..3453f8ab 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -296,7 +296,7 @@ def new_port(request, switchid): @login_required @can_edit(Port) -def edit_port(request, port_object, _portid): +def edit_port(request, port_object, **_kwargs): """ Edition d'un port. Permet de changer le switch parent et l'affectation du port""" @@ -322,7 +322,7 @@ def edit_port(request, port_object, _portid): @login_required @can_delete(Port) -def del_port(request, port, _portid): +def del_port(request, port, **_kwargs): """ Supprime le port""" if request.method == "POST": try: @@ -358,7 +358,7 @@ def new_stack(request): @login_required @can_edit(Stack) -def edit_stack(request, stack, _stackid): +def edit_stack(request, stack, **_kwargs): """Edition d'un stack (nombre de switches, nom...)""" stack = StackForm(request.POST or None, instance=stack) if stack.is_valid(): @@ -374,7 +374,7 @@ def edit_stack(request, stack, _stackid): @login_required @can_delete(Stack) -def del_stack(request, stack, _stackid): +def del_stack(request, stack, **_kwargs): """Supprime un stack""" if request.method == "POST": try: @@ -392,7 +392,7 @@ def del_stack(request, stack, _stackid): @login_required @can_edit(Stack) -def edit_switchs_stack(request, stack, _stackid): +def edit_switchs_stack(request, stack, **_kwargs): """Permet d'éditer la liste des switches dans une stack et l'ajouter""" if request.method == "POST": @@ -599,7 +599,7 @@ def new_ap(request): @login_required @can_edit(AccessPoint) -def edit_ap(request, ap, _accesspointid): +def edit_ap(request, ap, **_kwargs): """ Edition d'un switch. Permet de chambre nombre de ports, place dans le stack, interface et machine associée""" interface_form = EditInterfaceForm( @@ -668,7 +668,7 @@ def new_room(request): @login_required @can_edit(Room) -def edit_room(request, room, _roomid): +def edit_room(request, room, **_kwargs): """ Edition numero et details de la chambre""" room = EditRoomForm(request.POST or None, instance=room) if room.is_valid(): @@ -685,7 +685,7 @@ def edit_room(request, room, _roomid): @login_required @can_delete(Room) -def del_room(request, room, _roomid): +def del_room(request, room, **_kwargs): """ Suppression d'un chambre""" if request.method == "POST": try: @@ -723,7 +723,7 @@ def new_model_switch(request): @login_required @can_edit(ModelSwitch) -def edit_model_switch(request, model_switch, _modelswitchid): +def edit_model_switch(request, model_switch, **_kwargs): """ Edition d'un modèle de switch""" model_switch = EditModelSwitchForm( @@ -744,7 +744,7 @@ def edit_model_switch(request, model_switch, _modelswitchid): @login_required @can_delete(ModelSwitch) -def del_model_switch(request, model_switch, _modelswitchid): +def del_model_switch(request, model_switch, **_kwargs): """ Suppression d'un modèle de switch""" if request.method == "POST": try: @@ -782,7 +782,7 @@ def new_switch_bay(request): @login_required @can_edit(SwitchBay) -def edit_switch_bay(request, switch_bay, _switchbayid): +def edit_switch_bay(request, switch_bay, **_kwargs): """ Edition d'une baie de switch""" switch_bay = EditSwitchBayForm(request.POST or None, instance=switch_bay) if switch_bay.is_valid(): @@ -799,7 +799,7 @@ def edit_switch_bay(request, switch_bay, _switchbayid): @login_required @can_delete(SwitchBay) -def del_switch_bay(request, switch_bay, _switchbayid): +def del_switch_bay(request, switch_bay, **_kwargs): """ Suppression d'une baie de switch""" if request.method == "POST": try: @@ -837,7 +837,7 @@ def new_building(request): @login_required @can_edit(Building) -def edit_building(request, building, _buildingid): +def edit_building(request, building, **_kwargs): """ Edition d'un batiment""" building = EditBuildingForm(request.POST or None, instance=building) if building.is_valid(): @@ -854,7 +854,7 @@ def edit_building(request, building, _buildingid): @login_required @can_delete(Building) -def del_building(request, building, _buildingid): +def del_building(request, building, **_kwargs): """ Suppression d'un batiment""" if request.method == "POST": try: @@ -892,7 +892,7 @@ def new_constructor_switch(request): @login_required @can_edit(ConstructorSwitch) -def edit_constructor_switch(request, constructor_switch, _constructorswitchid): +def edit_constructor_switch(request, constructor_switch, **_kwargs): """ Edition d'un constructeur de switch""" constructor_switch = EditConstructorSwitchForm( @@ -913,7 +913,7 @@ def edit_constructor_switch(request, constructor_switch, _constructorswitchid): @login_required @can_delete(ConstructorSwitch) -def del_constructor_switch(request, constructor_switch, _constructorswitchid): +def del_constructor_switch(request, constructor_switch, **_kwargs): """ Suppression d'un constructeur de switch""" if request.method == "POST": try: diff --git a/users/models.py b/users/models.py index 47203a29..55ae0e8d 100644 --- a/users/models.py +++ b/users/models.py @@ -993,7 +993,7 @@ class Club(User): @receiver(post_save, sender=Adherent) @receiver(post_save, sender=Club) @receiver(post_save, sender=User) -def user_post_save(_sender, **kwargs): +def user_post_save(**kwargs): """ Synchronisation post_save : envoie le mail de bienvenue si creation Synchronise le ldap""" # is_created = kwargs['created'] @@ -1013,7 +1013,7 @@ def user_post_save(_sender, **kwargs): @receiver(post_delete, sender=Adherent) @receiver(post_delete, sender=Club) @receiver(post_delete, sender=User) -def user_post_delete(_sender, **kwargs): +def user_post_delete(**kwargs): """Post delete d'un user, on supprime son instance ldap""" user = kwargs['instance'] user.ldap_del() @@ -1100,14 +1100,14 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): @receiver(post_save, sender=ServiceUser) -def service_user_post_save(_sender, **kwargs): +def service_user_post_save(**kwargs): """ Synchronise un service user ldap après modification django""" service_user = kwargs['instance'] service_user.ldap_sync() @receiver(post_delete, sender=ServiceUser) -def service_user_post_delete(_sender, **kwargs): +def service_user_post_delete(**kwargs): """ Supprime un service user ldap après suppression django""" service_user = kwargs['instance'] service_user.ldap_del() @@ -1182,14 +1182,14 @@ class ListRight(RevMixin, AclMixin, Group): @receiver(post_save, sender=ListRight) -def listright_post_save(_sender, **kwargs): +def listright_post_save(**kwargs): """ Synchronise le droit ldap quand il est modifié""" right = kwargs['instance'] right.ldap_sync() @receiver(post_delete, sender=ListRight) -def listright_post_delete(_sender, **kwargs): +def listright_post_delete(**kwargs): """Suppression d'un groupe ldap après suppression coté django""" right = kwargs['instance'] right.ldap_del() @@ -1282,7 +1282,7 @@ class Ban(RevMixin, AclMixin, models.Model): @receiver(post_save, sender=Ban) -def ban_post_save(_sender, **kwargs): +def ban_post_save(**kwargs): """ Regeneration de tous les services après modification d'un ban""" ban = kwargs['instance'] is_created = kwargs['created'] @@ -1299,7 +1299,7 @@ def ban_post_save(_sender, **kwargs): @receiver(post_delete, sender=Ban) -def ban_post_delete(_sender, **kwargs): +def ban_post_delete(**kwargs): """ Regen de tous les services après suppression d'un ban""" user = kwargs['instance'].user user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) @@ -1348,7 +1348,7 @@ class Whitelist(RevMixin, AclMixin, models.Model): @receiver(post_save, sender=Whitelist) -def whitelist_post_save(_sender, **kwargs): +def whitelist_post_save(**kwargs): """Après modification d'une whitelist, on synchronise les services et on lui permet d'avoir internet""" whitelist = kwargs['instance'] @@ -1365,7 +1365,7 @@ def whitelist_post_save(_sender, **kwargs): @receiver(post_delete, sender=Whitelist) -def whitelist_post_delete(_sender, **kwargs): +def whitelist_post_delete(**kwargs): """Après suppression d'une whitelist, on supprime l'accès internet en forçant la régénration""" user = kwargs['instance'].user diff --git a/users/views.py b/users/views.py index a7abf653..a86a6e47 100644 --- a/users/views.py +++ b/users/views.py @@ -158,7 +158,7 @@ def new_club(request): @login_required @can_edit(Club) -def edit_club_admin_members(request, club_instance, _clubid): +def edit_club_admin_members(request, club_instance, **_kwargs): """Vue d'edition de la liste des users administrateurs et membres d'un club""" club = ClubAdminandMembersForm( @@ -286,7 +286,7 @@ def password(request, user, userid): @login_required @can_edit(User, 'groups') -def del_group(request, user, _userid, listrightid): +def del_group(request, user, listrightid, **_kwargs): """ View used to delete a group """ user.groups.remove(ListRight.objects.get(id=listrightid)) user.save() @@ -317,7 +317,7 @@ def new_serviceuser(request): @login_required @can_edit(ServiceUser) -def edit_serviceuser(request, serviceuser, _serviceuserid): +def edit_serviceuser(request, serviceuser, **_kwargs): """ Edit a ServiceUser """ serviceuser = EditServiceUserForm( request.POST or None, @@ -340,7 +340,7 @@ def edit_serviceuser(request, serviceuser, _serviceuserid): @login_required @can_delete(ServiceUser) -def del_serviceuser(request, serviceuser, _serviceuserid): +def del_serviceuser(request, serviceuser, **_kwargs): """Suppression d'un ou plusieurs serviceusers""" if request.method == "POST": serviceuser.delete() @@ -383,7 +383,7 @@ def add_ban(request, user, userid): @login_required @can_edit(Ban) -def edit_ban(request, ban_instance, _banid): +def edit_ban(request, ban_instance, **_kwargs): """ Editer un bannissement, nécessite au moins le droit bofh (a fortiori bureau) Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement""" @@ -434,7 +434,7 @@ def add_whitelist(request, user, userid): @login_required @can_edit(Whitelist) -def edit_whitelist(request, whitelist_instance, _whitelistid): +def edit_whitelist(request, whitelist_instance, **_kwargs): """ Editer un accès gracieux, temporaire ou permanent. Need droit cableur Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement, @@ -474,7 +474,7 @@ def add_school(request): @login_required @can_edit(School) -def edit_school(request, school_instance, _schoolid): +def edit_school(request, school_instance, **_kwargs): """ Editer un établissement d'enseignement à partir du schoolid dans la base de donnée, need cableur""" school = SchoolForm(request.POST or None, instance=school_instance) @@ -535,7 +535,7 @@ def add_shell(request): @login_required @can_edit(ListShell) -def edit_shell(request, shell_instance, _listshellid): +def edit_shell(request, shell_instance, **_kwargs): """ Editer un shell à partir du listshellid""" shell = ShellForm(request.POST or None, instance=shell_instance) if shell.is_valid(): @@ -552,7 +552,7 @@ def edit_shell(request, shell_instance, _listshellid): @login_required @can_delete(ListShell) -def del_shell(request, shell, _listshellid): +def del_shell(request, shell, **_kwargs): """Destruction d'un shell""" if request.method == "POST": shell.delete() @@ -584,7 +584,7 @@ def add_listright(request): @login_required @can_edit(ListRight) -def edit_listright(request, listright_instance, _listrightid): +def edit_listright(request, listright_instance, **_kwargs): """ Editer un groupe/droit, necessite droit bureau, à partir du listright id """ listright = ListRightForm( @@ -798,7 +798,7 @@ def mon_profil(request): @login_required @can_view(User) -def profil(request, users, _userid): +def profil(request, users, **_kwargs): """ Affiche un profil, self or cableur, prend un userid en argument """ machines = Machine.objects.filter(user=users).select_related('user')\ .prefetch_related('interface_set__domain__extension')\ From 915d5e893177ff644b097788350936db13761aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sun, 15 Apr 2018 10:51:05 +0000 Subject: [PATCH 20/22] Fix: user.models.User.auto_uid can't be static --- users/migrations/0035_auto_20161018_0046.py | 2 +- users/migrations/0056_auto_20171015_2033.py | 2 +- users/models.py | 11 ++++------- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/users/migrations/0035_auto_20161018_0046.py b/users/migrations/0035_auto_20161018_0046.py index 90387af6..aea0d13d 100644 --- a/users/migrations/0035_auto_20161018_0046.py +++ b/users/migrations/0035_auto_20161018_0046.py @@ -37,6 +37,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='user', name='uid_number', - field=models.IntegerField(unique=True, default=users.models.User.auto_uid), + field=models.IntegerField(unique=True, default=users.models.get_fresh_user_uid), ), ] diff --git a/users/migrations/0056_auto_20171015_2033.py b/users/migrations/0056_auto_20171015_2033.py index a47aca6a..90423340 100644 --- a/users/migrations/0056_auto_20171015_2033.py +++ b/users/migrations/0056_auto_20171015_2033.py @@ -32,6 +32,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='user', name='uid_number', - field=models.PositiveIntegerField(default=users.models.User.auto_uid, unique=True), + field=models.PositiveIntegerField(default=users.models.get_fresh_user_uid, unique=True), ), ] diff --git a/users/models.py b/users/models.py index 55ae0e8d..f7bfc128 100644 --- a/users/models.py +++ b/users/models.py @@ -187,12 +187,6 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, (2, 'STATE_ARCHIVE'), ) - # TODO : Use only one of auto_uid and get_fresh_user_uid - @staticmethod - def auto_uid(): - """Renvoie un uid libre""" - return get_fresh_user_uid() - surname = models.CharField(max_length=255) pseudo = models.CharField( max_length=32, @@ -222,7 +216,10 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, state = models.IntegerField(choices=STATES, default=STATE_ACTIVE) registered = models.DateTimeField(auto_now_add=True) telephone = models.CharField(max_length=15, blank=True, null=True) - uid_number = models.PositiveIntegerField(default=auto_uid, unique=True) + uid_number = models.PositiveIntegerField( + default=get_fresh_user_uid, + unique=True + ) rezo_rez_uid = models.PositiveIntegerField( unique=True, blank=True, From 6c30c369435ee856accd99467c9198deacd1a4b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sun, 15 Apr 2018 10:54:53 +0000 Subject: [PATCH 21/22] Fix : missing migrations --- .../migrations/0078_auto_20180415_1252.py | 21 +++++++++++++++++++ users/migrations/0071_auto_20180415_1252.py | 21 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 machines/migrations/0078_auto_20180415_1252.py create mode 100644 users/migrations/0071_auto_20180415_1252.py diff --git a/machines/migrations/0078_auto_20180415_1252.py b/machines/migrations/0078_auto_20180415_1252.py new file mode 100644 index 00000000..909ae847 --- /dev/null +++ b/machines/migrations/0078_auto_20180415_1252.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-04-15 10:52 +from __future__ import unicode_literals + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0077_auto_20180409_2243'), + ] + + operations = [ + migrations.AlterField( + model_name='srv', + name='priority', + field=models.PositiveIntegerField(default=0, help_text="La priorité du serveur cible (valeur entière non négative, plus elle est faible, plus ce serveur sera utilisé s'il est disponible)", validators=[django.core.validators.MaxValueValidator(65535)]), + ), + ] diff --git a/users/migrations/0071_auto_20180415_1252.py b/users/migrations/0071_auto_20180415_1252.py new file mode 100644 index 00000000..faf56a5b --- /dev/null +++ b/users/migrations/0071_auto_20180415_1252.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-04-15 10:52 +from __future__ import unicode_literals + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0070_auto_20180324_1906'), + ] + + operations = [ + migrations.AlterField( + model_name='listright', + name='unix_name', + field=models.CharField(max_length=255, unique=True, validators=[django.core.validators.RegexValidator('^[a-z]+$', message='Les groupes unix ne peuvent contenir que des lettres minuscules')]), + ), + ] From f7f8f7493bb12987ed57f0a812d8c7f336246dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Sun, 15 Apr 2018 13:34:51 +0000 Subject: [PATCH 22/22] Fix : various little things --- cotisations/templates/cotisations/facture.html | 6 ++++-- cotisations/views.py | 2 +- re2o/acl.py | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cotisations/templates/cotisations/facture.html b/cotisations/templates/cotisations/facture.html index d708b407..dc8648ac 100644 --- a/cotisations/templates/cotisations/facture.html +++ b/cotisations/templates/cotisations/facture.html @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %} + {% if articlesformset %}

{% trans "Invoice's articles" %}

{{ articlesformset.management_form }} @@ -54,11 +55,12 @@ with this program; if not, write to the Free Software Foundation, Inc., Total price : 0,00 € {% endblocktrans %}

+ {% endif %} {% bootstrap_form factureform %} {% bootstrap_button action_name button_type='submit' icon='star' %} - +{% if articlesformset %} - +{% endif %} {% endblock %} diff --git a/cotisations/views.py b/cotisations/views.py index 5687a0fa..78ee0d74 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -489,7 +489,7 @@ def add_paiement(request): # TODO : chnage paiement to Payment @login_required @can_edit(Paiement) -def edit_paiement(request, paiement_instance, *_kwargs): +def edit_paiement(request, paiement_instance, **_kwargs): """ View used to edit a payment method. """ diff --git a/re2o/acl.py b/re2o/acl.py index 0acc710a..6ab34350 100644 --- a/re2o/acl.py +++ b/re2o/acl.py @@ -171,13 +171,13 @@ def can_delete_set(model): all_objects = model.objects.all() instances_id = [] for instance in all_objects: - can, msg = instance.can_delete(request.user) + can, _msg = instance.can_delete(request.user) if can: instances_id.append(instance.id) instances = model.objects.filter(id__in=instances_id) if not instances: messages.error( - request, msg or "Vous ne pouvez pas accéder à ce menu") + request, "Vous ne pouvez pas accéder à ce menu") return redirect(reverse( 'users:profil', kwargs={'userid': str(request.user.id)}