Commit 332e8a34 authored by Maël Kervella's avatar Maël Kervella

Pylint compliance on preferences

parent e88141db
# -*- 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 * from .acl import *
# 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 string
import binascii import binascii
from random import choice from random import choice
...@@ -10,10 +41,13 @@ EOD = '`%EofD%`' # This should be something that will not occur in strings ...@@ -10,10 +41,13 @@ EOD = '`%EofD%`' # This should be something that will not occur in strings
def genstring(length=16, chars=string.printable): 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)]) return ''.join([choice(chars) for i in range(length)])
def encrypt(key, s): def encrypt(key, s):
""" AES Encrypt a secret `s` with the key `key` """
obj = AES.new(key) obj = AES.new(key)
datalength = len(s) + len(EOD) datalength = len(s) + len(EOD)
if datalength < 16: if datalength < 16:
...@@ -25,12 +59,15 @@ def encrypt(key, s): ...@@ -25,12 +59,15 @@ def encrypt(key, s):
def decrypt(key, s): def decrypt(key, s):
""" AES Decrypt a secret `s` with the key `key` """
obj = AES.new(key) obj = AES.new(key)
ss = obj.decrypt(s) ss = obj.decrypt(s)
return ss.split(bytes(EOD, 'utf-8'))[0] return ss.split(bytes(EOD, 'utf-8'))[0]
class AESEncryptedField(models.CharField): 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): def save_form_data(self, instance, data):
setattr(instance, self.name, setattr(instance, self.name,
binascii.b2a_base64(encrypt(settings.AES_KEY, data))) binascii.b2a_base64(encrypt(settings.AES_KEY, data)))
...@@ -41,16 +78,10 @@ class AESEncryptedField(models.CharField): ...@@ -41,16 +78,10 @@ class AESEncryptedField(models.CharField):
return decrypt(settings.AES_KEY, return decrypt(settings.AES_KEY,
binascii.a2b_base64(value)).decode('utf-8') 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): def get_prep_value(self, value):
if value is None: if value is None:
return value return value
return binascii.b2a_base64(encrypt( return binascii.b2a_base64(encrypt(
settings.AES_KEY, settings.AES_KEY,
value value
)) ))
from django.apps import AppConfig
class PreferencesConfig(AppConfig):
name = 'preferences'
...@@ -27,25 +27,31 @@ from __future__ import unicode_literals ...@@ -27,25 +27,31 @@ from __future__ import unicode_literals
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.db import models from django.db import models
import cotisations.models from django.db.models.signals import post_save
import machines.models
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from django.core.cache import cache from django.core.cache import cache
from .aes_field import AESEncryptedField import cotisations.models
import machines.models
from re2o.mixins import AclMixin from re2o.mixins import AclMixin
from .aes_field import AESEncryptedField
class PreferencesModel(models.Model): 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 @classmethod
def set_in_cache(cls): def set_in_cache(cls):
""" Save the preferences in a server-side cache """
instance, _created = cls.objects.get_or_create() instance, _created = cls.objects.get_or_create()
cache.set(cls().__class__.__name__.lower(), instance, None) cache.set(cls().__class__.__name__.lower(), instance, None)
return instance return instance
@classmethod @classmethod
def get_cached_value(cls, key): def get_cached_value(cls, key):
""" Get the preferences from the server-side cache """
instance = cache.get(cls().__class__.__name__.lower()) instance = cache.get(cls().__class__.__name__.lower())
if instance is None: if instance is None:
instance = cls.set_in_cache() instance = cls.set_in_cache()
...@@ -112,7 +118,7 @@ class OptionalUser(AclMixin, PreferencesModel): ...@@ -112,7 +118,7 @@ class OptionalUser(AclMixin, PreferencesModel):
@receiver(post_save, sender=OptionalUser) @receiver(post_save, sender=OptionalUser)
def optionaluser_post_save(sender, **kwargs): def optionaluser_post_save(_sender, **kwargs):
"""Ecriture dans le cache""" """Ecriture dans le cache"""
user_pref = kwargs['instance'] user_pref = kwargs['instance']
user_pref.set_in_cache() user_pref.set_in_cache()
...@@ -147,6 +153,7 @@ class OptionalMachine(AclMixin, PreferencesModel): ...@@ -147,6 +153,7 @@ class OptionalMachine(AclMixin, PreferencesModel):
@cached_property @cached_property
def ipv6(self): def ipv6(self):
""" Check if the IPv6 option is activated """
return not self.get_cached_value('ipv6_mode') == 'DISABLED' return not self.get_cached_value('ipv6_mode') == 'DISABLED'
class Meta: class Meta:
...@@ -156,7 +163,7 @@ class OptionalMachine(AclMixin, PreferencesModel): ...@@ -156,7 +163,7 @@ class OptionalMachine(AclMixin, PreferencesModel):
@receiver(post_save, sender=OptionalMachine) @receiver(post_save, sender=OptionalMachine)
def optionalmachine_post_save(sender, **kwargs): def optionalmachine_post_save(_sender, **kwargs):
"""Synchronisation ipv6 et ecriture dans le cache""" """Synchronisation ipv6 et ecriture dans le cache"""
machine_pref = kwargs['instance'] machine_pref = kwargs['instance']
machine_pref.set_in_cache() machine_pref.set_in_cache()
...@@ -204,7 +211,7 @@ class OptionalTopologie(AclMixin, PreferencesModel): ...@@ -204,7 +211,7 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@receiver(post_save, sender=OptionalTopologie) @receiver(post_save, sender=OptionalTopologie)
def optionaltopologie_post_save(sender, **kwargs): def optionaltopologie_post_save(_sender, **kwargs):
"""Ecriture dans le cache""" """Ecriture dans le cache"""
topologie_pref = kwargs['instance'] topologie_pref = kwargs['instance']
topologie_pref.set_in_cache() topologie_pref.set_in_cache()
...@@ -244,7 +251,7 @@ class GeneralOption(AclMixin, PreferencesModel): ...@@ -244,7 +251,7 @@ class GeneralOption(AclMixin, PreferencesModel):
@receiver(post_save, sender=GeneralOption) @receiver(post_save, sender=GeneralOption)
def generaloption_post_save(sender, **kwargs): def generaloption_post_save(_sender, **kwargs):
"""Ecriture dans le cache""" """Ecriture dans le cache"""
general_pref = kwargs['instance'] general_pref = kwargs['instance']
general_pref.set_in_cache() general_pref.set_in_cache()
...@@ -318,7 +325,7 @@ class AssoOption(AclMixin, PreferencesModel): ...@@ -318,7 +325,7 @@ class AssoOption(AclMixin, PreferencesModel):
@receiver(post_save, sender=AssoOption) @receiver(post_save, sender=AssoOption)
def assooption_post_save(sender, **kwargs): def assooption_post_save(_sender, **kwargs):
"""Ecriture dans le cache""" """Ecriture dans le cache"""
asso_pref = kwargs['instance'] asso_pref = kwargs['instance']
asso_pref.set_in_cache() asso_pref.set_in_cache()
......
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. # Create your tests here.
...@@ -27,8 +27,8 @@ from __future__ import unicode_literals ...@@ -27,8 +27,8 @@ from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from . import views
import re2o import re2o
from . import views
urlpatterns = [ urlpatterns = [
......
...@@ -31,17 +31,17 @@ topologie, users, service...) ...@@ -31,17 +31,17 @@ topologie, users, service...)
from __future__ import unicode_literals from __future__ import unicode_literals
from django.urls import reverse from django.urls import reverse
from django.shortcuts import render, redirect from django.shortcuts import redirect
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required
from django.db.models import ProtectedError from django.db.models import ProtectedError
from django.db import transaction from django.db import transaction
from reversion.models import Version
from reversion import revisions as reversion from reversion import revisions as reversion
from re2o.views import form from re2o.views import form
from re2o.acl import can_create, can_edit, can_delete_set, can_view_all from re2o.acl import can_create, can_edit, can_delete_set, can_view_all
from .forms import ServiceForm, DelServiceForm from .forms import ServiceForm, DelServiceForm
from .models import Service, OptionalUser, OptionalMachine, AssoOption from .models import Service, OptionalUser, OptionalMachine, AssoOption
from .models import MailMessageOption, GeneralOption, OptionalTopologie from .models import MailMessageOption, GeneralOption, OptionalTopologie
...@@ -136,7 +136,7 @@ def add_service(request): ...@@ -136,7 +136,7 @@ def add_service(request):
@login_required @login_required
@can_edit(Service) @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""" """Edition des services affichés sur la page d'accueil"""
service = ServiceForm(request.POST or None, instance=service_instance) service = ServiceForm(request.POST or None, instance=service_instance)
if service.is_valid(): if service.is_valid():
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment