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] 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