Commit 2ca271bf authored by Maël Kervella's avatar Maël Kervella

Pylint compliance on re2o

parent 332e8a34
......@@ -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
* ...
"""
This diff is collapsed.
#!/usr/bin/env python3
"""re2o.contributors
A list of the proud contributors to Re2o
"""
CONTRIBUTORS = [
'Gabriel "Chirac" Détraz',
......
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
......@@ -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):
......
......@@ -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))
......@@ -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
......
......@@ -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 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
from django.utils.html import strip_tags
from users.models import User
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)
......
......@@ -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,
}
......@@ -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 = ()
......@@ -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:]
......
......@@ -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
......@@ -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):
......
......@@ -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,
......
......@@ -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
......
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