8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-04 17:06:27 +00:00

Pylint compliance on re2o

This commit is contained in:
Maël Kervella 2018-04-14 19:29:16 +00:00
parent 332e8a3413
commit 2ca271bf82
15 changed files with 295 additions and 181 deletions

View file

@ -20,3 +20,15 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""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
* ...
"""

View file

@ -33,14 +33,6 @@ from django.contrib import messages
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse from django.urls import reverse
import cotisations
import logs
import machines
import preferences
import search
import topologie
import users
def can_create(model): def can_create(model):
"""Decorator to check if an user can create a model. """Decorator to check if an user can create a model.
@ -49,7 +41,11 @@ def can_create(model):
of models. of models.
""" """
def decorator(view): def decorator(view):
"""The decorator to use on a specific view
"""
def wrapper(request, *args, **kwargs): def wrapper(request, *args, **kwargs):
"""The wrapper used for a specific request
"""
can, msg = model.can_create(request.user, *args, **kwargs) can, msg = model.can_create(request.user, *args, **kwargs)
if not can: if not can:
messages.error( messages.error(
@ -68,31 +64,37 @@ def can_edit(model, *field_list):
kind of models. kind of models.
""" """
def decorator(view): def decorator(view):
"""The decorator to use on a specific view
"""
def wrapper(request, *args, **kwargs): def wrapper(request, *args, **kwargs):
"""The wrapper used for a specific request
"""
try: try:
instance = model.get_instance(*args, **kwargs) instance = model.get_instance(*args, **kwargs)
except model.DoesNotExist: except model.DoesNotExist:
messages.error(request, u"Entrée inexistante") messages.error(request, u"Entrée inexistante")
return redirect(reverse('users:profil', return redirect(reverse(
kwargs={'userid': str(request.user.id)} 'users:profil',
)) kwargs={'userid': str(request.user.id)}
))
can, msg = instance.can_edit(request.user) can, msg = instance.can_edit(request.user)
if not can: if not can:
messages.error( messages.error(
request, msg or "Vous ne pouvez pas accéder à ce menu") request, msg or "Vous ne pouvez pas accéder à ce menu")
return redirect(reverse('users:profil', return redirect(reverse(
kwargs={'userid': str(request.user.id)} 'users:profil',
)) kwargs={'userid': str(request.user.id)}
))
for field in field_list: for field in field_list:
can_change = getattr(instance, 'can_change_' + field) can_change_fct = getattr(instance, 'can_change_' + field)
can, msg = can_change(request.user, *args, **kwargs) can, msg = can_change_fct(request.user, *args, **kwargs)
if not can: if not can:
messages.error( messages.error(
request, msg or "Vous ne pouvez pas accéder à ce menu") request, msg or "Vous ne pouvez pas accéder à ce menu")
return redirect(reverse('users:profil', return redirect(reverse(
kwargs={'userid': str( 'users:profil',
request.user.id)} kwargs={'userid': str(request.user.id)}
)) ))
return view(request, instance, *args, **kwargs) return view(request, instance, *args, **kwargs)
return wrapper return wrapper
return decorator return decorator
@ -103,17 +105,21 @@ def can_change(model, *field_list):
Difference with can_edit : take a class and not an instance Difference with can_edit : take a class and not an instance
""" """
def decorator(view): def decorator(view):
"""The decorator to use on a specific view
"""
def wrapper(request, *args, **kwargs): def wrapper(request, *args, **kwargs):
"""The wrapper used for a specific request
"""
for field in field_list: for field in field_list:
can_change = getattr(model, 'can_change_' + field) can_change_fct = getattr(model, 'can_change_' + field)
can, msg = can_change(request.user, *args, **kwargs) can, msg = can_change_fct(request.user, *args, **kwargs)
if not can: if not can:
messages.error( messages.error(
request, msg or "Vous ne pouvez pas accéder à ce menu") request, msg or "Vous ne pouvez pas accéder à ce menu")
return redirect(reverse('users:profil', return redirect(reverse(
kwargs={'userid': str( 'users:profil',
request.user.id)} kwargs={'userid': str(request.user.id)}
)) ))
return view(request, *args, **kwargs) return view(request, *args, **kwargs)
return wrapper return wrapper
return decorator return decorator
@ -127,21 +133,27 @@ def can_delete(model):
kind of models. kind of models.
""" """
def decorator(view): def decorator(view):
"""The decorator to use on a specific view
"""
def wrapper(request, *args, **kwargs): def wrapper(request, *args, **kwargs):
"""The wrapper used for a specific request
"""
try: try:
instance = model.get_instance(*args, **kwargs) instance = model.get_instance(*args, **kwargs)
except model.DoesNotExist: except model.DoesNotExist:
messages.error(request, u"Entrée inexistante") messages.error(request, u"Entrée inexistante")
return redirect(reverse('users:profil', return redirect(reverse(
kwargs={'userid': str(request.user.id)} 'users:profil',
)) kwargs={'userid': str(request.user.id)}
))
can, msg = instance.can_delete(request.user) can, msg = instance.can_delete(request.user)
if not can: if not can:
messages.error( messages.error(
request, msg or "Vous ne pouvez pas accéder à ce menu") request, msg or "Vous ne pouvez pas accéder à ce menu")
return redirect(reverse('users:profil', return redirect(reverse(
kwargs={'userid': str(request.user.id)} 'users:profil',
)) kwargs={'userid': str(request.user.id)}
))
return view(request, instance, *args, **kwargs) return view(request, instance, *args, **kwargs)
return wrapper return wrapper
return decorator return decorator
@ -151,7 +163,11 @@ def can_delete_set(model):
"""Decorator which returns a list of detable models by request user. """Decorator which returns a list of detable models by request user.
If none of them, return an error""" If none of them, return an error"""
def decorator(view): def decorator(view):
"""The decorator to use on a specific view
"""
def wrapper(request, *args, **kwargs): def wrapper(request, *args, **kwargs):
"""The wrapper used for a specific request
"""
all_objects = model.objects.all() all_objects = model.objects.all()
instances_id = [] instances_id = []
for instance in all_objects: for instance in all_objects:
@ -160,10 +176,12 @@ def can_delete_set(model):
instances_id.append(instance.id) instances_id.append(instance.id)
instances = model.objects.filter(id__in=instances_id) instances = model.objects.filter(id__in=instances_id)
if not instances: if not instances:
messages.error(request, "Vous ne pouvez pas accéder à ce menu") messages.error(
return redirect(reverse('users:profil', request, msg or "Vous ne pouvez pas accéder à ce menu")
kwargs={'userid': str(request.user.id)} return redirect(reverse(
)) 'users:profil',
kwargs={'userid': str(request.user.id)}
))
return view(request, instances, *args, **kwargs) return view(request, instances, *args, **kwargs)
return wrapper return wrapper
return decorator return decorator
@ -177,21 +195,27 @@ def can_view(model):
kind of models. kind of models.
""" """
def decorator(view): def decorator(view):
"""The decorator to use on a specific view
"""
def wrapper(request, *args, **kwargs): def wrapper(request, *args, **kwargs):
"""The wrapper used for a specific request
"""
try: try:
instance = model.get_instance(*args, **kwargs) instance = model.get_instance(*args, **kwargs)
except model.DoesNotExist: except model.DoesNotExist:
messages.error(request, u"Entrée inexistante") messages.error(request, u"Entrée inexistante")
return redirect(reverse('users:profil', return redirect(reverse(
kwargs={'userid': str(request.user.id)} 'users:profil',
)) kwargs={'userid': str(request.user.id)}
))
can, msg = instance.can_view(request.user) can, msg = instance.can_view(request.user)
if not can: if not can:
messages.error( messages.error(
request, msg or "Vous ne pouvez pas accéder à ce menu") request, msg or "Vous ne pouvez pas accéder à ce menu")
return redirect(reverse('users:profil', return redirect(reverse(
kwargs={'userid': str(request.user.id)} 'users:profil',
)) kwargs={'userid': str(request.user.id)}
))
return view(request, instance, *args, **kwargs) return view(request, instance, *args, **kwargs)
return wrapper return wrapper
return decorator return decorator
@ -201,14 +225,19 @@ def can_view_all(model):
"""Decorator to check if an user can view a class of model. """Decorator to check if an user can view a class of model.
""" """
def decorator(view): def decorator(view):
"""The decorator to use on a specific view
"""
def wrapper(request, *args, **kwargs): def wrapper(request, *args, **kwargs):
"""The wrapper used for a specific request
"""
can, msg = model.can_view_all(request.user) can, msg = model.can_view_all(request.user)
if not can: if not can:
messages.error( messages.error(
request, msg or "Vous ne pouvez pas accéder à ce menu") request, msg or "Vous ne pouvez pas accéder à ce menu")
return redirect(reverse('users:profil', return redirect(reverse(
kwargs={'userid': str(request.user.id)} 'users:profil',
)) kwargs={'userid': str(request.user.id)}
))
return view(request, *args, **kwargs) return view(request, *args, **kwargs)
return wrapper return wrapper
return decorator return decorator
@ -220,15 +249,20 @@ def can_view_app(app_name):
assert app_name in sys.modules.keys() assert app_name in sys.modules.keys()
def decorator(view): def decorator(view):
"""The decorator to use on a specific view
"""
def wrapper(request, *args, **kwargs): def wrapper(request, *args, **kwargs):
"""The wrapper used for a specific request
"""
app = sys.modules[app_name] app = sys.modules[app_name]
can, msg = app.can_view(request.user) can, msg = app.can_view(request.user)
if can: if can:
return view(request, *args, **kwargs) return view(request, *args, **kwargs)
messages.error(request, msg) messages.error(request, msg)
return redirect(reverse('users:profil', return redirect(reverse(
kwargs={'userid': str(request.user.id)} 'users:profil',
)) kwargs={'userid': str(request.user.id)}
))
return wrapper return wrapper
return decorator return decorator
@ -236,13 +270,16 @@ def can_view_app(app_name):
def can_edit_history(view): def can_edit_history(view):
"""Decorator to check if an user can edit history.""" """Decorator to check if an user can edit history."""
def wrapper(request, *args, **kwargs): def wrapper(request, *args, **kwargs):
"""The wrapper used for a specific request
"""
if request.user.has_perm('admin.change_logentry'): if request.user.has_perm('admin.change_logentry'):
return view(request, *args, **kwargs) return view(request, *args, **kwargs)
messages.error( messages.error(
request, request,
"Vous ne pouvez pas éditer l'historique." "Vous ne pouvez pas éditer l'historique."
) )
return redirect(reverse('users:profil', return redirect(reverse(
kwargs={'userid': str(request.user.id)} 'users:profil',
)) kwargs={'userid': str(request.user.id)}
))
return wrapper return wrapper

View file

@ -1,4 +1,6 @@
#!/usr/bin/env python3 """re2o.contributors
A list of the proud contributors to Re2o
"""
CONTRIBUTORS = [ CONTRIBUTORS = [
'Gabriel "Chirac" Détraz', 'Gabriel "Chirac" Détraz',

View file

@ -1,15 +1,45 @@
from django.db import models # -*- mode: python; coding: utf-8 -*-
from django import forms # Re2o est un logiciel d'administration développé initiallement au rezometz. Il
from functools import partial # 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: class FieldPermissionModelMixin:
""" The model mixin. Defines the `has_field_perm` function """
field_permissions = {} # {'field_name': callable} field_permissions = {} # {'field_name': callable}
FIELD_PERM_CODENAME = 'can_change_{model}_{name}' FIELD_PERM_CODENAME = 'can_change_{model}_{name}'
FIELD_PERMISSION_GETTER = 'can_change_{name}' FIELD_PERMISSION_GETTER = 'can_change_{name}'
FIELD_PERMISSION_MISSING_DEFAULT = True FIELD_PERMISSION_MISSING_DEFAULT = True
def has_field_perm(self, user, field): def has_field_perm(self, user, field):
""" Checks if a `user` has the right to edit the `field`
of this model """
if field in self.field_permissions: if field in self.field_permissions:
checks = self.field_permissions[field] checks = self.field_permissions[field]
if not isinstance(checks, (list, tuple)): if not isinstance(checks, (list, tuple)):
@ -39,7 +69,7 @@ class FieldPermissionModelMixin:
# Try to find a user setting that qualifies them for permission. # Try to find a user setting that qualifies them for permission.
for perm in checks: for perm in checks:
if callable(perm): if callable(perm):
result, reason = perm(user_request=user) result, _reason = perm(user_request=user)
if result is not None: if result is not None:
return result return result
else: else:
@ -52,11 +82,6 @@ class FieldPermissionModelMixin:
return False return False
class FieldPermissionModel(FieldPermissionModelMixin, models.Model):
class Meta:
abstract = True
class FieldPermissionFormMixin: class FieldPermissionFormMixin:
""" """
Construit le formulaire et retire les champs interdits Construit le formulaire et retire les champs interdits
@ -73,8 +98,5 @@ class FieldPermissionFormMixin:
self.remove_unauthorized_field(name) self.remove_unauthorized_field(name)
def remove_unauthorized_field(self, name): def remove_unauthorized_field(self, name):
""" Remove one field from the fields of the form """
del self.fields[name] del self.fields[name]
class FieldPermissionForm(FieldPermissionFormMixin, forms.ModelForm):
pass

View file

@ -24,7 +24,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Module d'authentification # Module d'authentification
# David Sinquin, Gabriel Détraz, Goulven Kermarec # David Sinquin, Gabriel Détraz, Goulven Kermarec
"""re2o.login
Module in charge of handling the login process and verifications
"""
import hashlib import hashlib
import binascii import binascii
@ -42,6 +44,7 @@ DIGEST_LEN = 20
def makeSecret(password): def makeSecret(password):
""" Build a hashed and salted version of the password """
salt = os.urandom(4) salt = os.urandom(4)
h = hashlib.sha1(password.encode()) h = hashlib.sha1(password.encode())
h.update(salt) h.update(salt)
@ -49,11 +52,13 @@ def makeSecret(password):
def hashNT(password): def hashNT(password):
hash = hashlib.new('md4', password.encode('utf-16le')).digest() """ Build a md4 hash of the password to use as the NT-password """
return binascii.hexlify(hash).upper() hash_str = hashlib.new('md4', password.encode('utf-16le')).digest()
return binascii.hexlify(hash_str).upper()
def checkPassword(challenge_password, password): 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()) challenge_bytes = decodestring(challenge_password[ALGO_LEN:].encode())
digest = challenge_bytes[:DIGEST_LEN] digest = challenge_bytes[:DIGEST_LEN]
salt = challenge_bytes[DIGEST_LEN:] salt = challenge_bytes[DIGEST_LEN:]
@ -74,7 +79,7 @@ class SSHAPasswordHasher(hashers.BasePasswordHasher):
algorithm = ALGO_NAME algorithm = ALGO_NAME
def encode(self, password, salt, iterations=None): def encode(self, password, salt):
""" """
Hash and salt the given password using SSHA algorithm Hash and salt the given password using SSHA algorithm
@ -92,16 +97,16 @@ class SSHAPasswordHasher(hashers.BasePasswordHasher):
def safe_summary(self, encoded): def safe_summary(self, encoded):
""" """
Provides a safe summary ofthe password Provides a safe summary of the password
""" """
assert encoded.startswith(self.algorithm) assert encoded.startswith(self.algorithm)
hash = encoded[ALGO_LEN:] hash_str = encoded[ALGO_LEN:]
hash = binascii.hexlify(decodestring(hash.encode())).decode() hash_str = binascii.hexlify(decodestring(hash_str.encode())).decode()
return OrderedDict([ return OrderedDict([
('algorithm', self.algorithm), ('algorithm', self.algorithm),
('iterations', 0), ('iterations', 0),
('salt', hashers.mask_hash(hash[2*DIGEST_LEN:], show=2)), ('salt', hashers.mask_hash(hash_str[2*DIGEST_LEN:], show=2)),
('hash', hashers.mask_hash(hash[:2*DIGEST_LEN])), ('hash', hashers.mask_hash(hash_str[:2*DIGEST_LEN])),
]) ])
def harden_runtime(self, password, encoded): def harden_runtime(self, password, encoded):

View file

@ -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. commits. This list is extracted from the current gitlab repository.
""" """
from django.core.management.base import BaseCommand, CommandError
import os import os
from django.core.management.base import BaseCommand
class Command(BaseCommand): class Command(BaseCommand):
""" The command object for `gen_contrib` """
help = 'Update contributors list' help = 'Update contributors list'
def handle(self, *args, **options): def handle(self, *args, **options):
@ -39,6 +40,8 @@ class Command(BaseCommand):
] ]
self.stdout.write(self.style.SUCCESS("Exportation Sucessfull")) self.stdout.write(self.style.SUCCESS("Exportation Sucessfull"))
with open("re2o/contributors.py", "w") as contrib_file: 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("\n")
contrib_file.write("CONTRIBUTORS = " + str(contributeurs)) contrib_file.write("CONTRIBUTORS = " + str(contributeurs))

View file

@ -19,23 +19,34 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""re2o.mixins
A set of mixins used all over the project to avoid duplicating code
"""
from reversion import revisions as reversion from reversion import revisions as reversion
class RevMixin(object): 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): def save(self, *args, **kwargs):
""" Creates a version of this object and save it to database """
if self.pk is None: if self.pk is None:
reversion.set_comment("Création") reversion.set_comment("Création")
return super(RevMixin, self).save(*args, **kwargs) return super(RevMixin, self).save(*args, **kwargs)
def delete(self, *args, **kwargs): def delete(self, *args, **kwargs):
""" Creates a version of this object and delete it from database """
reversion.set_comment("Suppresion") reversion.set_comment("Suppresion")
return super(RevMixin, self).delete(*args, **kwargs) return super(RevMixin, self).delete(*args, **kwargs)
class FormRevMixin(object): 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): def save(self, *args, **kwargs):
""" Create a version of this object and save it to database """
if reversion.get_comment() != "" and self.changed_data != []: if reversion.get_comment() != "" and self.changed_data != []:
reversion.set_comment( reversion.set_comment(
reversion.get_comment() + ",%s" reversion.get_comment() + ",%s"
@ -66,14 +77,16 @@ class AclMixin(object):
@classmethod @classmethod
def get_classname(cls): def get_classname(cls):
""" Returns the name of the class where this mixin is used """
return str(cls.__name__).lower() return str(cls.__name__).lower()
@classmethod @classmethod
def get_modulename(cls): def get_modulename(cls):
""" Returns the name of the module where this mixin is used """
return str(cls.__module__).split('.')[0].lower() return str(cls.__module__).split('.')[0].lower()
@classmethod @classmethod
def get_instance(cls, *args, **kwargs): def get_instance(cls, *_args, **kwargs):
"""Récupère une instance """Récupère une instance
:param objectid: Instance id à trouver :param objectid: Instance id à trouver
:return: Une instance de la classe évidemment""" :return: Une instance de la classe évidemment"""
@ -81,7 +94,7 @@ class AclMixin(object):
return cls.objects.get(pk=object_id) return cls.objects.get(pk=object_id)
@classmethod @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 """Verifie que l'user a les bons droits pour créer
un object un object
:param user_request: instance utilisateur qui fait la requête :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() 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 """Verifie que l'user a les bons droits pour editer
cette instance cette instance
:param self: Instance à editer :param self: Instance à editer
@ -106,7 +119,7 @@ class AclMixin(object):
u"Vous n'avez pas le droit d'éditer des " + self.get_classname() 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 """Verifie que l'user a les bons droits pour delete
cette instance cette instance
:param self: Instance à delete :param self: Instance à delete
@ -120,7 +133,7 @@ class AclMixin(object):
) )
@classmethod @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, """Vérifie qu'on peut bien afficher l'ensemble des objets,
droit particulier view objet correspondant droit particulier view objet correspondant
:param user_request: instance user qui fait l'edition :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() 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 """Vérifie qu'on peut bien voir cette instance particulière avec
droit view objet droit view objet
:param self: instance à voir :param self: instance à voir

View file

@ -18,22 +18,27 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""re2o.script_utils
A set of utility scripts that can be used as standalone to interact easily
with Re2o throught the CLI
"""
import os import os
from os.path import dirname
import sys import sys
import pwd 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.core.management.base import CommandError
from django.db import transaction
from django.utils.html import strip_tags
from users.models import User from users.models import User
from django.utils.html import strip_tags proj_path = dirname(dirname(__file__))
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") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "re2o.settings")
sys.path.append(proj_path) sys.path.append(proj_path)
os.chdir(proj_path) os.chdir(proj_path)

View file

@ -35,38 +35,37 @@ https://docs.djangoproject.com/en/1.8/ref/settings/
from __future__ import unicode_literals from __future__ import unicode_literals
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os import os
from .settings_local import * 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__))) 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 # Auth definition
PASSWORD_HASHERS = ( PASSWORD_HASHERS = (
're2o.login.SSHAPasswordHasher', 're2o.login.SSHAPasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
) )
AUTH_USER_MODEL = 'users.User' # The class to use for authentication
AUTH_USER_MODEL = 'users.User' LOGIN_URL = '/login/' # The URL for login page
LOGIN_URL = '/login/' LOGIN_REDIRECT_URL = '/' # The URL for redirecting after login
LOGIN_REDIRECT_URL = '/'
# Application definition # Application definition
DJANGO_CONTRIB_APPS = (
INSTALLED_APPS = (
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
)
EXTERNAL_CONTRIB_APPS = (
'bootstrap3', 'bootstrap3',
'rest_framework',
'reversion',
)
LOCAL_APPS = (
'users', 'users',
'machines', 'machines',
'cotisations', 'cotisations',
@ -75,11 +74,14 @@ INSTALLED_APPS = (
're2o', 're2o',
'preferences', 'preferences',
'logs', 'logs',
'rest_framework', 'api',
'reversion', )
'api' INSTALLED_APPS = (
) + OPTIONNAL_APPS DJANGO_CONTRIB_APPS +
EXTERNAL_CONTRIB_APPS +
LOCAL_APPS +
OPTIONNAL_APPS
)
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware', 'django.middleware.locale.LocaleMiddleware',
@ -93,14 +95,17 @@ MIDDLEWARE_CLASSES = (
'reversion.middleware.RevisionMiddleware', 'reversion.middleware.RevisionMiddleware',
) )
# The root url module to define the project URLs
ROOT_URLCONF = 're2o.urls' ROOT_URLCONF = 're2o.urls'
# The templates configuration (see Django documentation)
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [ '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, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
@ -115,57 +120,50 @@ TEMPLATES = [
}, },
] ]
# The WSGI module to use in a server environment
WSGI_APPLICATION = 're2o.wsgi.application' WSGI_APPLICATION = 're2o.wsgi.application'
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/ # https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'en' LANGUAGE_CODE = 'en'
USE_I18N = True
USE_L10N = True
# Proritary location search for translations # Proritary location search for translations
# then searches in {app}/locale/ for app in INSTALLED_APPS # then searches in {app}/locale/ for app in INSTALLED_APPS
# Use only absolute paths with '/' delimiters even on Windows
LOCALE_PATHS = [ 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' # Should use time zone ?
USE_I18N = True
USE_L10N = True
USE_TZ = True USE_TZ = True
# Router config for database
DATABASE_ROUTERS = ['ldapdb.router.Router'] DATABASE_ROUTERS = ['ldapdb.router.Router']
# django-bootstrap3 config
# django-bootstrap3 config dictionnary
BOOTSTRAP3 = { BOOTSTRAP3 = {
'jquery_url': '/static/js/jquery-2.2.4.min.js', 'jquery_url': '/static/js/jquery-2.2.4.min.js',
'base_url': '/static/bootstrap/', 'base_url': '/static/bootstrap/',
'include_jquery': True, 'include_jquery': True,
} }
BOOTSTRAP_BASE_URL = '/static/bootstrap/' BOOTSTRAP_BASE_URL = '/static/bootstrap/'
# Directories where collectstatic should look for static files
# Use only absolute paths with '/' delimiters even on Windows
STATICFILES_DIRS = ( STATICFILES_DIRS = (
# Put strings here, like "/home/html/static" or "C:/www/django/static". os.path.join(BASE_DIR, 'static').replace('\\', '/'),
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
os.path.join(
BASE_DIR,
'static',
),
) )
# Directory where the static files serverd by the server are stored
MEDIA_ROOT = '/var/www/re2o/media'
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static_files') 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 = { GRAPH_MODELS = {
'all_applications': True, 'all_applications': True,
'group_models': True, 'group_models': True,
} }

View file

@ -19,45 +19,56 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""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 from __future__ import unicode_literals
# A secret key used by the server.
SECRET_KEY = 'SUPER_SECRET_KEY' SECRET_KEY = 'SUPER_SECRET_KEY'
# The password to access the project database
DB_PASSWORD = 'SUPER_SECRET_DB' DB_PASSWORD = 'SUPER_SECRET_DB'
# AES key for secret key encryption length must be a multiple of 16 # AES key for secret key encryption.
AES_KEY = 'THE_AES_KEY' # 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! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False DEBUG = False
# A list of admins of the services. Receive mails when an error occurs
ADMINS = [('Example', 'rezo-admin@example.org')] ADMINS = [('Example', 'rezo-admin@example.org')]
SERVER_EMAIL = 'no-reply@example.org' # The list of hostname the server will respond to.
# Obligatoire, liste des host autorisés
ALLOWED_HOSTS = ['URL_SERVER'] ALLOWED_HOSTS = ['URL_SERVER']
# The time zone the server is runned in
TIME_ZONE = 'Europe/Paris'
# The storage systems parameters to use
DATABASES = { DATABASES = {
'default': { 'default': { # The DB
'ENGINE': 'db_engine', 'ENGINE': 'db_engine',
'NAME': 'db_name_value', 'NAME': 'db_name_value',
'USER': 'db_user_value', 'USER': 'db_user_value',
'PASSWORD': DB_PASSWORD, 'PASSWORD': DB_PASSWORD,
'HOST': 'db_host_value', 'HOST': 'db_host_value',
}, },
'ldap': { 'ldap': { # The LDAP
'ENGINE': 'ldapdb.backends.ldap', 'ENGINE': 'ldapdb.backends.ldap',
'NAME': 'ldap://ldap_host_ip/', 'NAME': 'ldap://ldap_host_ip/',
'USER': 'ldap_dn', 'USER': 'ldap_dn',
# 'TLS': True, 'TLS': True,
'PASSWORD': 'SUPER_SECRET_LDAP', '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_CONTENT_TYPE_NOSNIFF = False
SECURE_BROWSER_XSS_FILTER = False SECURE_BROWSER_XSS_FILTER = False
SESSION_COOKIE_SECURE = False SESSION_COOKIE_SECURE = False
@ -66,12 +77,15 @@ CSRF_COOKIE_HTTPONLY = False
X_FRAME_OPTIONS = 'DENY' X_FRAME_OPTIONS = 'DENY'
SESSION_COOKIE_AGE = 60 * 60 * 3 SESSION_COOKIE_AGE = 60 * 60 * 3
# The path where your organization logo is stored
LOGO_PATH = "static_files/logo.png" LOGO_PATH = "static_files/logo.png"
EMAIL_HOST = 'MY_EMAIL_HOST' # The mail configuration for Re2o to send mails
EMAIL_PORT = MY_EMAIL_PORT 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 = { LDAP = {
'base_user_dn': 'cn=Utilisateurs,dc=example,dc=org', 'base_user_dn': 'cn=Utilisateurs,dc=example,dc=org',
'base_userservice_dn': 'ou=service-users,dc=example,dc=org', 'base_userservice_dn': 'ou=service-users,dc=example,dc=org',
@ -80,15 +94,16 @@ LDAP = {
'user_gid': 500, 'user_gid': 500,
} }
# A range of UID to use. Used in linux environement
UID_RANGES = { UID_RANGES = {
'users': [21001, 30000], 'users': [21001, 30000],
'service-users': [20000, 21000], '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 = { GID_RANGES = {
'posix': [501, 600], 'posix': [501, 600],
} }
# Some Django apps you want to add in you local project
OPTIONNAL_APPS = () OPTIONNAL_APPS = ()

View file

@ -302,7 +302,7 @@ def acl_change_filter(parser, token):
try: try:
tag_content = token.split_contents() tag_content = token.split_contents()
tag_name = tag_content[0] # tag_name = tag_content[0]
model_name = tag_content[1] model_name = tag_content[1]
field_name = tag_content[2] field_name = tag_content[2]
args = tag_content[3:] args = tag_content[3:]

View file

@ -19,13 +19,20 @@
# You should have received a copy of the GNU General Public License along # You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""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 django import template
from preferences.models import OptionalUser, GeneralOption from preferences.models import OptionalUser
register = template.Library() register = template.Library()
@register.simple_tag @register.simple_tag
def self_adhesion(): def self_adhesion():
""" Returns True if the user are allowed to create accounts """
options, _created = OptionalUser.objects.get_or_create() options, _created = OptionalUser.objects.get_or_create()
return options.self_adhesion return options.self_adhesion

View file

@ -36,18 +36,13 @@ Fonction :
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils import timezone from django.utils import timezone
from django.db.models import Q 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 django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from cotisations.models import Cotisation, Facture, Paiement, Vente from cotisations.models import Cotisation, Facture, Vente
from machines.models import Domain, Interface, Machine from machines.models import Interface, Machine
from users.models import Adherent, User, Ban, Whitelist from users.models import Adherent, User, Ban, Whitelist
from preferences.models import Service
def all_adherent(search_time=None): def all_adherent(search_time=None):

View file

@ -26,32 +26,28 @@ les views
from __future__ import unicode_literals from __future__ import unicode_literals
from itertools import chain
import git
from reversion.models import Version
from django.http import Http404 from django.http import Http404
from django.urls import reverse from django.urls import reverse
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.template.context_processors import csrf from django.template.context_processors import csrf
from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.decorators import login_required
from reversion.models import Version
from django.contrib import messages from django.contrib import messages
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.decorators.cache import cache_page 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 import preferences
from preferences.models import Service, GeneralOption, AssoOption
import users
import cotisations import cotisations
import topologie import topologie
import machines import machines
from .utils import re2o_paginator from .utils import re2o_paginator
from .settings import BASE_DIR, INSTALLED_APPS, MIDDLEWARE_CLASSES
from .contributors import CONTRIBUTORS from .contributors import CONTRIBUTORS
@ -143,7 +139,7 @@ def history(request, application, object_name, object_id):
""" """
try: try:
model = HISTORY_BIND[application][object_name] 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.") raise Http404(u"Il n'existe pas d'historique pour ce modèle.")
object_name_id = object_name + 'id' object_name_id = object_name + 'id'
kwargs = {object_name_id: object_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) @cache_page(7 * 24 * 60 * 60)
def about_page(request): 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() option = AssoOption.objects.get()
git_info_contributors = CONTRIBUTORS git_info_contributors = CONTRIBUTORS
try: try:
git_repo = git.Repo(BASE_DIR) git_repo = git.Repo(settings.BASE_DIR)
git_info_remote = ", ".join(git_repo.remote().urls) git_info_remote = ", ".join(git_repo.remote().urls)
git_info_branch = git_repo.active_branch.name git_info_branch = git_repo.active_branch.name
last_commit = git_repo.commit() last_commit = git_repo.commit()
@ -194,7 +193,7 @@ def about_page(request):
git_info_commit = NO_GIT_MSG git_info_commit = NO_GIT_MSG
git_info_commit_date = NO_GIT_MSG git_info_commit_date = NO_GIT_MSG
dependencies = INSTALLED_APPS + MIDDLEWARE_CLASSES dependencies = settings.INSTALLED_APPS + settings.MIDDLEWARE_CLASSES
return render( return render(
request, request,

View file

@ -32,8 +32,9 @@ https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/
from __future__ import unicode_literals from __future__ import unicode_literals
import os import os
import sys
from os.path import dirname from os.path import dirname
import sys
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application