mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-30 08:32:26 +00:00
Merge branch 'fix_bugs_divers' into 'dev'
Fix bugs divers See merge request federez/re2o!356
This commit is contained in:
commit
f6ac6e4c85
16 changed files with 325 additions and 263 deletions
|
@ -1000,6 +1000,17 @@ class CNAMERecordSerializer(serializers.ModelSerializer):
|
||||||
model = machines.Domain
|
model = machines.Domain
|
||||||
fields = ('alias', 'hostname')
|
fields = ('alias', 'hostname')
|
||||||
|
|
||||||
|
class DNAMERecordSerializer(serializers.ModelSerializer):
|
||||||
|
"""Serialize `machines.models.Domain` objects with the data needed to
|
||||||
|
generate a DNAME DNS record.
|
||||||
|
"""
|
||||||
|
alias = serializers.CharField(read_only=True)
|
||||||
|
zone = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = machines.DName
|
||||||
|
fields = ('alias', 'zone')
|
||||||
|
|
||||||
|
|
||||||
class DNSZonesSerializer(serializers.ModelSerializer):
|
class DNSZonesSerializer(serializers.ModelSerializer):
|
||||||
"""Serialize the data about DNS Zones.
|
"""Serialize the data about DNS Zones.
|
||||||
|
@ -1014,14 +1025,14 @@ class DNSZonesSerializer(serializers.ModelSerializer):
|
||||||
a_records = ARecordSerializer(many=True, source='get_associated_a_records')
|
a_records = ARecordSerializer(many=True, source='get_associated_a_records')
|
||||||
aaaa_records = AAAARecordSerializer(many=True, source='get_associated_aaaa_records')
|
aaaa_records = AAAARecordSerializer(many=True, source='get_associated_aaaa_records')
|
||||||
cname_records = CNAMERecordSerializer(many=True, source='get_associated_cname_records')
|
cname_records = CNAMERecordSerializer(many=True, source='get_associated_cname_records')
|
||||||
|
dname_records = DNAMERecordSerializer(many=True, source='get_associated_dname_records')
|
||||||
sshfp_records = SSHFPInterfaceSerializer(many=True, source='get_associated_sshfp_records')
|
sshfp_records = SSHFPInterfaceSerializer(many=True, source='get_associated_sshfp_records')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = machines.Extension
|
model = machines.Extension
|
||||||
fields = ('name', 'soa', 'ns_records', 'originv4', 'originv6',
|
fields = ('name', 'soa', 'ns_records', 'originv4', 'originv6',
|
||||||
'mx_records', 'txt_records', 'srv_records', 'a_records',
|
'mx_records', 'txt_records', 'srv_records', 'a_records',
|
||||||
'aaaa_records', 'cname_records', 'sshfp_records')
|
'aaaa_records', 'cname_records', 'dname_records', 'sshfp_records')
|
||||||
|
|
||||||
#REMINDER
|
#REMINDER
|
||||||
|
|
||||||
|
|
||||||
|
|
10
api/views.py
10
api/views.py
|
@ -541,8 +541,8 @@ class ServiceRegenViewSet(viewsets.ModelViewSet):
|
||||||
# Config des switches
|
# Config des switches
|
||||||
|
|
||||||
class SwitchPortView(generics.ListAPIView):
|
class SwitchPortView(generics.ListAPIView):
|
||||||
"""Exposes the associations between hostname, mac address and IPv4 in
|
"""Output each port of a switch, to be serialized with
|
||||||
order to build the DHCP lease files.
|
additionnal informations (profiles etc)
|
||||||
"""
|
"""
|
||||||
queryset = topologie.Switch.objects.all().select_related("switchbay").select_related("model__constructor").prefetch_related("ports__custom_profile__vlan_tagged").prefetch_related("ports__custom_profile__vlan_untagged").prefetch_related("ports__machine_interface__domain__extension").prefetch_related("ports__room")
|
queryset = topologie.Switch.objects.all().select_related("switchbay").select_related("model__constructor").prefetch_related("ports__custom_profile__vlan_tagged").prefetch_related("ports__custom_profile__vlan_untagged").prefetch_related("ports__machine_interface__domain__extension").prefetch_related("ports__room")
|
||||||
|
|
||||||
|
@ -551,16 +551,14 @@ class SwitchPortView(generics.ListAPIView):
|
||||||
# Rappel fin adhésion
|
# Rappel fin adhésion
|
||||||
|
|
||||||
class ReminderView(generics.ListAPIView):
|
class ReminderView(generics.ListAPIView):
|
||||||
"""Exposes the associations between hostname, mac address and IPv4 in
|
"""Output for users to remind an end of their subscription.
|
||||||
order to build the DHCP lease files.
|
|
||||||
"""
|
"""
|
||||||
queryset = preferences.Reminder.objects.all()
|
queryset = preferences.Reminder.objects.all()
|
||||||
serializer_class = serializers.ReminderSerializer
|
serializer_class = serializers.ReminderSerializer
|
||||||
|
|
||||||
|
|
||||||
class RoleView(generics.ListAPIView):
|
class RoleView(generics.ListAPIView):
|
||||||
"""Exposes the associations between hostname, mac address and IPv4 in
|
"""Output of roles for each server
|
||||||
order to build the DHCP lease files.
|
|
||||||
"""
|
"""
|
||||||
queryset = machines.Role.objects.all().prefetch_related('servers')
|
queryset = machines.Role.objects.all().prefetch_related('servers')
|
||||||
serializer_class = serializers.RoleSerializer
|
serializer_class = serializers.RoleSerializer
|
||||||
|
|
|
@ -47,7 +47,10 @@ from users.models import User
|
||||||
from re2o.settings import LOGO_PATH
|
from re2o.settings import LOGO_PATH
|
||||||
from re2o import settings
|
from re2o import settings
|
||||||
from re2o.views import form
|
from re2o.views import form
|
||||||
from re2o.utils import SortTable, re2o_paginator
|
from re2o.base import (
|
||||||
|
SortTable,
|
||||||
|
re2o_paginator,
|
||||||
|
)
|
||||||
from re2o.acl import (
|
from re2o.acl import (
|
||||||
can_create,
|
can_create,
|
||||||
can_edit,
|
can_edit,
|
||||||
|
|
|
@ -102,15 +102,18 @@ from re2o.utils import (
|
||||||
all_baned,
|
all_baned,
|
||||||
all_has_access,
|
all_has_access,
|
||||||
all_adherent,
|
all_adherent,
|
||||||
|
all_active_assigned_interfaces_count,
|
||||||
|
all_active_interfaces_count,
|
||||||
|
)
|
||||||
|
from re2o.base import (
|
||||||
re2o_paginator,
|
re2o_paginator,
|
||||||
|
SortTable
|
||||||
)
|
)
|
||||||
from re2o.acl import (
|
from re2o.acl import (
|
||||||
can_view_all,
|
can_view_all,
|
||||||
can_view_app,
|
can_view_app,
|
||||||
can_edit_history,
|
can_edit_history,
|
||||||
)
|
)
|
||||||
from re2o.utils import all_active_assigned_interfaces_count
|
|
||||||
from re2o.utils import all_active_interfaces_count, SortTable
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
|
|
@ -741,6 +741,9 @@ class Extension(RevMixin, AclMixin, models.Model):
|
||||||
.filter(cname__interface_parent__in=all_active_assigned_interfaces())
|
.filter(cname__interface_parent__in=all_active_assigned_interfaces())
|
||||||
.prefetch_related('cname'))
|
.prefetch_related('cname'))
|
||||||
|
|
||||||
|
def get_associated_dname_records(self):
|
||||||
|
return (DName.objects.filter(alias=self))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_use_all(user_request, *_args, **_kwargs):
|
def can_use_all(user_request, *_args, **_kwargs):
|
||||||
"""Superdroit qui permet d'utiliser toutes les extensions sans
|
"""Superdroit qui permet d'utiliser toutes les extensions sans
|
||||||
|
@ -1631,18 +1634,6 @@ class Role(RevMixin, AclMixin, models.Model):
|
||||||
verbose_name = _("server role")
|
verbose_name = _("server role")
|
||||||
verbose_name_plural = _("server roles")
|
verbose_name_plural = _("server roles")
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_instance(cls, roleid, *_args, **_kwargs):
|
|
||||||
"""Get the Role instance with roleid.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
roleid: The id
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The role.
|
|
||||||
"""
|
|
||||||
return cls.objects.get(pk=roleid)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def interface_for_roletype(cls, roletype):
|
def interface_for_roletype(cls, roletype):
|
||||||
"""Return interfaces for a roletype"""
|
"""Return interfaces for a roletype"""
|
||||||
|
@ -1657,14 +1648,6 @@ class Role(RevMixin, AclMixin, models.Model):
|
||||||
machine__interface__role=cls.objects.filter(specific_role=roletype)
|
machine__interface__role=cls.objects.filter(specific_role=roletype)
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_instance(cls, roleid, *_args, **_kwargs):
|
|
||||||
"""Get the Machine instance with machineid.
|
|
||||||
:param userid: The id
|
|
||||||
:return: The user
|
|
||||||
"""
|
|
||||||
return cls.objects.get(pk=roleid)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def interface_for_roletype(cls, roletype):
|
def interface_for_roletype(cls, roletype):
|
||||||
"""Return interfaces for a roletype"""
|
"""Return interfaces for a roletype"""
|
||||||
|
|
|
@ -95,9 +95,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% if interfaceform %}
|
{% if interfaceform %}
|
||||||
<h3>{% trans "Interface" %}</h3>
|
<h3>{% trans "Interface" %}</h3>
|
||||||
{% if i_mbf_param %}
|
{% if i_mbf_param %}
|
||||||
{% massive_bootstrap_form interfaceform 'ipv4,machine' mbf_param=i_mbf_param %}
|
{% massive_bootstrap_form interfaceform 'ipv4,machine,port_lists' mbf_param=i_mbf_param %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% massive_bootstrap_form interfaceform 'ipv4,machine' %}
|
{% massive_bootstrap_form interfaceform 'ipv4,machine,port_lists' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if domainform %}
|
{% if domainform %}
|
||||||
|
@ -146,7 +146,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if aliasform %}
|
{% if aliasform %}
|
||||||
<h3>{% trans "Alias" %}</h3>
|
<h3>{% trans "Alias" %}</h3>
|
||||||
{% bootstrap_form aliasform %}
|
{% massive_bootstrap_form aliasform 'extension' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if serviceform %}
|
{% if serviceform %}
|
||||||
<h3>{% trans "Service" %}</h3>
|
<h3>{% trans "Service" %}</h3>
|
||||||
|
|
|
@ -55,6 +55,8 @@ from re2o.acl import (
|
||||||
from re2o.utils import (
|
from re2o.utils import (
|
||||||
all_active_assigned_interfaces,
|
all_active_assigned_interfaces,
|
||||||
filter_active_interfaces,
|
filter_active_interfaces,
|
||||||
|
)
|
||||||
|
from re2o.base import (
|
||||||
SortTable,
|
SortTable,
|
||||||
re2o_paginator,
|
re2o_paginator,
|
||||||
)
|
)
|
||||||
|
|
267
re2o/base.py
Normal file
267
re2o/base.py
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
# -*- 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 © 2018 Gabriel Détraz
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Regroupe les fonctions transversales utiles
|
||||||
|
|
||||||
|
Et non corrélées/dépendantes des autres applications
|
||||||
|
"""
|
||||||
|
|
||||||
|
import smtplib
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||||
|
|
||||||
|
from re2o.settings import EMAIL_HOST
|
||||||
|
|
||||||
|
|
||||||
|
# Mapping of srtftime format for better understanding
|
||||||
|
# https://docs.python.org/3.6/library/datetime.html#strftime-strptime-behavior
|
||||||
|
datetime_mapping={
|
||||||
|
'%a': '%a',
|
||||||
|
'%A': '%A',
|
||||||
|
'%w': '%w',
|
||||||
|
'%d': 'dd',
|
||||||
|
'%b': '%b',
|
||||||
|
'%B': '%B',
|
||||||
|
'%m': 'mm',
|
||||||
|
'%y': 'yy',
|
||||||
|
'%Y': 'yyyy',
|
||||||
|
'%H': 'HH',
|
||||||
|
'%I': 'HH(12h)',
|
||||||
|
'%p': 'AMPM',
|
||||||
|
'%M': 'MM',
|
||||||
|
'%S': 'SS',
|
||||||
|
'%f': 'µµ',
|
||||||
|
'%z': 'UTC(+/-HHMM)',
|
||||||
|
'%Z': 'UTC(TZ)',
|
||||||
|
'%j': '%j',
|
||||||
|
'%U': 'ww',
|
||||||
|
'%W': 'ww',
|
||||||
|
'%c': '%c',
|
||||||
|
'%x': '%x',
|
||||||
|
'%X': '%X',
|
||||||
|
'%%': '%%',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def smtp_check(local_part):
|
||||||
|
"""Return True if the local_part is already taken
|
||||||
|
False if available"""
|
||||||
|
try:
|
||||||
|
srv = smtplib.SMTP(EMAIL_HOST)
|
||||||
|
srv.putcmd("vrfy", local_part)
|
||||||
|
reply_code = srv.getreply()[0]
|
||||||
|
srv.close()
|
||||||
|
if reply_code in [250, 252]:
|
||||||
|
return True, _("This domain is already taken")
|
||||||
|
except:
|
||||||
|
return True, _("Smtp unreachable")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
|
||||||
|
def convert_datetime_format(format):
|
||||||
|
i=0
|
||||||
|
new_format = ""
|
||||||
|
while i < len(format):
|
||||||
|
if format[i] == '%':
|
||||||
|
char = format[i:i+2]
|
||||||
|
new_format += datetime_mapping.get(char, char)
|
||||||
|
i += 2
|
||||||
|
else:
|
||||||
|
new_format += format[i]
|
||||||
|
i += 1
|
||||||
|
return new_format
|
||||||
|
|
||||||
|
|
||||||
|
def get_input_formats_help_text(input_formats):
|
||||||
|
"""Returns a help text about the possible input formats"""
|
||||||
|
if len(input_formats) > 1:
|
||||||
|
help_text_template="Format: {main} {more}"
|
||||||
|
else:
|
||||||
|
help_text_template="Format: {main}"
|
||||||
|
more_text_template="<i class=\"fa fa-question-circle\" title=\"{}\"></i>"
|
||||||
|
help_text = help_text_template.format(
|
||||||
|
main=convert_datetime_format(input_formats[0]),
|
||||||
|
more=more_text_template.format(
|
||||||
|
'\n'.join(map(convert_datetime_format, input_formats))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return help_text
|
||||||
|
|
||||||
|
|
||||||
|
class SortTable:
|
||||||
|
""" Class gathering uselful stuff to sort the colums of a table, according
|
||||||
|
to the column and order requested. It's used with a dict of possible
|
||||||
|
values and associated model_fields """
|
||||||
|
|
||||||
|
# All the possible possible values
|
||||||
|
# The naming convention is based on the URL or the views function
|
||||||
|
# The syntax to describe the sort to apply is a dict where the keys are
|
||||||
|
# the url value and the values are a list of model field name to use to
|
||||||
|
# order the request. They are applied in the order they are given.
|
||||||
|
# A 'default' might be provided to specify what to do if the requested col
|
||||||
|
# doesn't match any keys.
|
||||||
|
|
||||||
|
USERS_INDEX = {
|
||||||
|
'user_name': ['name'],
|
||||||
|
'user_surname': ['surname'],
|
||||||
|
'user_pseudo': ['pseudo'],
|
||||||
|
'user_room': ['room'],
|
||||||
|
'default': ['state', 'pseudo']
|
||||||
|
}
|
||||||
|
USERS_INDEX_BAN = {
|
||||||
|
'ban_user': ['user__pseudo'],
|
||||||
|
'ban_start': ['date_start'],
|
||||||
|
'ban_end': ['date_end'],
|
||||||
|
'default': ['-date_end']
|
||||||
|
}
|
||||||
|
USERS_INDEX_WHITE = {
|
||||||
|
'white_user': ['user__pseudo'],
|
||||||
|
'white_start': ['date_start'],
|
||||||
|
'white_end': ['date_end'],
|
||||||
|
'default': ['-date_end']
|
||||||
|
}
|
||||||
|
USERS_INDEX_SCHOOL = {
|
||||||
|
'school_name': ['name'],
|
||||||
|
'default': ['name']
|
||||||
|
}
|
||||||
|
MACHINES_INDEX = {
|
||||||
|
'machine_name': ['name'],
|
||||||
|
'default': ['pk']
|
||||||
|
}
|
||||||
|
COTISATIONS_INDEX = {
|
||||||
|
'cotis_user': ['user__pseudo'],
|
||||||
|
'cotis_paiement': ['paiement__moyen'],
|
||||||
|
'cotis_date': ['date'],
|
||||||
|
'cotis_id': ['id'],
|
||||||
|
'default': ['-date']
|
||||||
|
}
|
||||||
|
COTISATIONS_CUSTOM = {
|
||||||
|
'invoice_date': ['date'],
|
||||||
|
'invoice_id': ['id'],
|
||||||
|
'invoice_recipient': ['recipient'],
|
||||||
|
'invoice_address': ['address'],
|
||||||
|
'invoice_payment': ['payment'],
|
||||||
|
'default': ['-date']
|
||||||
|
}
|
||||||
|
COTISATIONS_CONTROL = {
|
||||||
|
'control_name': ['user__adherent__name'],
|
||||||
|
'control_surname': ['user__surname'],
|
||||||
|
'control_paiement': ['paiement'],
|
||||||
|
'control_date': ['date'],
|
||||||
|
'control_valid': ['valid'],
|
||||||
|
'control_control': ['control'],
|
||||||
|
'control_id': ['id'],
|
||||||
|
'control_user-id': ['user__id'],
|
||||||
|
'default': ['-date']
|
||||||
|
}
|
||||||
|
TOPOLOGIE_INDEX = {
|
||||||
|
'switch_dns': ['interface__domain__name'],
|
||||||
|
'switch_ip': ['interface__ipv4__ipv4'],
|
||||||
|
'switch_loc': ['switchbay__name'],
|
||||||
|
'switch_ports': ['number'],
|
||||||
|
'switch_stack': ['stack__name'],
|
||||||
|
'default': ['switchbay', 'stack', 'stack_member_id']
|
||||||
|
}
|
||||||
|
TOPOLOGIE_INDEX_PORT = {
|
||||||
|
'port_port': ['port'],
|
||||||
|
'port_room': ['room__name'],
|
||||||
|
'port_interface': ['machine_interface__domain__name'],
|
||||||
|
'port_related': ['related__switch__name'],
|
||||||
|
'port_radius': ['radius'],
|
||||||
|
'port_vlan': ['vlan_force__name'],
|
||||||
|
'default': ['port']
|
||||||
|
}
|
||||||
|
TOPOLOGIE_INDEX_ROOM = {
|
||||||
|
'room_name': ['name'],
|
||||||
|
'default': ['name']
|
||||||
|
}
|
||||||
|
TOPOLOGIE_INDEX_BUILDING = {
|
||||||
|
'building_name': ['name'],
|
||||||
|
'default': ['name']
|
||||||
|
}
|
||||||
|
TOPOLOGIE_INDEX_BORNE = {
|
||||||
|
'ap_name': ['interface__domain__name'],
|
||||||
|
'ap_ip': ['interface__ipv4__ipv4'],
|
||||||
|
'ap_mac': ['interface__mac_address'],
|
||||||
|
'default': ['interface__domain__name']
|
||||||
|
}
|
||||||
|
TOPOLOGIE_INDEX_STACK = {
|
||||||
|
'stack_name': ['name'],
|
||||||
|
'stack_id': ['stack_id'],
|
||||||
|
'default': ['stack_id'],
|
||||||
|
}
|
||||||
|
TOPOLOGIE_INDEX_MODEL_SWITCH = {
|
||||||
|
'model-switch_name': ['reference'],
|
||||||
|
'model-switch_contructor': ['constructor__name'],
|
||||||
|
'default': ['reference'],
|
||||||
|
}
|
||||||
|
TOPOLOGIE_INDEX_SWITCH_BAY = {
|
||||||
|
'switch-bay_name': ['name'],
|
||||||
|
'switch-bay_building': ['building__name'],
|
||||||
|
'default': ['name'],
|
||||||
|
}
|
||||||
|
TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH = {
|
||||||
|
'constructor-switch_name': ['name'],
|
||||||
|
'default': ['name'],
|
||||||
|
}
|
||||||
|
LOGS_INDEX = {
|
||||||
|
'sum_date': ['revision__date_created'],
|
||||||
|
'default': ['-revision__date_created'],
|
||||||
|
}
|
||||||
|
LOGS_STATS_LOGS = {
|
||||||
|
'logs_author': ['user__name'],
|
||||||
|
'logs_date': ['date_created'],
|
||||||
|
'default': ['-date_created']
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sort(request, col, order, values):
|
||||||
|
""" Check if the given values are possible and add .order_by() and
|
||||||
|
a .reverse() as specified according to those values """
|
||||||
|
fields = values.get(col, None)
|
||||||
|
if not fields:
|
||||||
|
fields = values.get('default', [])
|
||||||
|
request = request.order_by(*fields)
|
||||||
|
if values.get(col, None) and order == 'desc':
|
||||||
|
return request.reverse()
|
||||||
|
else:
|
||||||
|
return request
|
||||||
|
|
||||||
|
|
||||||
|
def re2o_paginator(request, query_set, pagination_number):
|
||||||
|
"""Paginator script for list display in re2o.
|
||||||
|
:request:
|
||||||
|
:query_set: Query_set to paginate
|
||||||
|
:pagination_number: Number of entries to display"""
|
||||||
|
paginator = Paginator(query_set, pagination_number)
|
||||||
|
page = request.GET.get('page')
|
||||||
|
try:
|
||||||
|
results = paginator.page(page)
|
||||||
|
except PageNotAnInteger:
|
||||||
|
# If page is not an integer, deliver first page.
|
||||||
|
results = paginator.page(1)
|
||||||
|
except EmptyPage:
|
||||||
|
# If page is out of range (e.g. 9999), deliver last page of results.
|
||||||
|
results = paginator.page(paginator.num_pages)
|
||||||
|
return results
|
|
@ -114,9 +114,9 @@ class CryptPasswordHasher(hashers.BasePasswordHasher):
|
||||||
Check password against encoded using CRYPT algorithm
|
Check password against encoded using CRYPT algorithm
|
||||||
"""
|
"""
|
||||||
assert encoded.startswith(self.algorithm)
|
assert encoded.startswith(self.algorithm)
|
||||||
salt = hash_password_salt(challenge_password)
|
salt = hash_password_salt(encoded)
|
||||||
return constant_time_compare(crypt.crypt(password.encode(), salt),
|
return constant_time_compare(crypt.crypt(password, salt),
|
||||||
challenge.encode())
|
encoded)
|
||||||
|
|
||||||
def safe_summary(self, encoded):
|
def safe_summary(self, encoded):
|
||||||
"""
|
"""
|
||||||
|
|
217
re2o/utils.py
217
re2o/utils.py
|
@ -38,55 +38,11 @@ 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.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
|
||||||
|
|
||||||
from cotisations.models import Cotisation, Facture, Vente
|
from cotisations.models import Cotisation, Facture, Vente
|
||||||
from machines.models import Interface, Machine
|
from machines.models import Interface, Machine
|
||||||
from users.models import Adherent, User, Ban, Whitelist
|
from users.models import Adherent, User, Ban, Whitelist
|
||||||
|
|
||||||
# Mapping of srtftime format for better understanding
|
|
||||||
# https://docs.python.org/3.6/library/datetime.html#strftime-strptime-behavior
|
|
||||||
datetime_mapping={
|
|
||||||
'%a': '%a',
|
|
||||||
'%A': '%A',
|
|
||||||
'%w': '%w',
|
|
||||||
'%d': 'dd',
|
|
||||||
'%b': '%b',
|
|
||||||
'%B': '%B',
|
|
||||||
'%m': 'mm',
|
|
||||||
'%y': 'yy',
|
|
||||||
'%Y': 'yyyy',
|
|
||||||
'%H': 'HH',
|
|
||||||
'%I': 'HH(12h)',
|
|
||||||
'%p': 'AMPM',
|
|
||||||
'%M': 'MM',
|
|
||||||
'%S': 'SS',
|
|
||||||
'%f': 'µµ',
|
|
||||||
'%z': 'UTC(+/-HHMM)',
|
|
||||||
'%Z': 'UTC(TZ)',
|
|
||||||
'%j': '%j',
|
|
||||||
'%U': 'ww',
|
|
||||||
'%W': 'ww',
|
|
||||||
'%c': '%c',
|
|
||||||
'%x': '%x',
|
|
||||||
'%X': '%X',
|
|
||||||
'%%': '%%',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def convert_datetime_format(format):
|
|
||||||
i=0
|
|
||||||
new_format = ""
|
|
||||||
while i < len(format):
|
|
||||||
if format[i] == '%':
|
|
||||||
char = format[i:i+2]
|
|
||||||
new_format += datetime_mapping.get(char, char)
|
|
||||||
i += 2
|
|
||||||
else:
|
|
||||||
new_format += format[i]
|
|
||||||
i += 1
|
|
||||||
return new_format
|
|
||||||
|
|
||||||
|
|
||||||
def all_adherent(search_time=None):
|
def all_adherent(search_time=None):
|
||||||
""" Fonction renvoyant tous les users adherents. Optimisee pour n'est
|
""" Fonction renvoyant tous les users adherents. Optimisee pour n'est
|
||||||
|
@ -203,164 +159,6 @@ def all_active_assigned_interfaces_count():
|
||||||
return all_active_interfaces_count().filter(ipv4__isnull=False)
|
return all_active_interfaces_count().filter(ipv4__isnull=False)
|
||||||
|
|
||||||
|
|
||||||
class SortTable:
|
|
||||||
""" Class gathering uselful stuff to sort the colums of a table, according
|
|
||||||
to the column and order requested. It's used with a dict of possible
|
|
||||||
values and associated model_fields """
|
|
||||||
|
|
||||||
# All the possible possible values
|
|
||||||
# The naming convention is based on the URL or the views function
|
|
||||||
# The syntax to describe the sort to apply is a dict where the keys are
|
|
||||||
# the url value and the values are a list of model field name to use to
|
|
||||||
# order the request. They are applied in the order they are given.
|
|
||||||
# A 'default' might be provided to specify what to do if the requested col
|
|
||||||
# doesn't match any keys.
|
|
||||||
|
|
||||||
USERS_INDEX = {
|
|
||||||
'user_name': ['name'],
|
|
||||||
'user_surname': ['surname'],
|
|
||||||
'user_pseudo': ['pseudo'],
|
|
||||||
'user_room': ['room'],
|
|
||||||
'default': ['state', 'pseudo']
|
|
||||||
}
|
|
||||||
USERS_INDEX_BAN = {
|
|
||||||
'ban_user': ['user__pseudo'],
|
|
||||||
'ban_start': ['date_start'],
|
|
||||||
'ban_end': ['date_end'],
|
|
||||||
'default': ['-date_end']
|
|
||||||
}
|
|
||||||
USERS_INDEX_WHITE = {
|
|
||||||
'white_user': ['user__pseudo'],
|
|
||||||
'white_start': ['date_start'],
|
|
||||||
'white_end': ['date_end'],
|
|
||||||
'default': ['-date_end']
|
|
||||||
}
|
|
||||||
USERS_INDEX_SCHOOL = {
|
|
||||||
'school_name': ['name'],
|
|
||||||
'default': ['name']
|
|
||||||
}
|
|
||||||
MACHINES_INDEX = {
|
|
||||||
'machine_name': ['name'],
|
|
||||||
'default': ['pk']
|
|
||||||
}
|
|
||||||
COTISATIONS_INDEX = {
|
|
||||||
'cotis_user': ['user__pseudo'],
|
|
||||||
'cotis_paiement': ['paiement__moyen'],
|
|
||||||
'cotis_date': ['date'],
|
|
||||||
'cotis_id': ['id'],
|
|
||||||
'default': ['-date']
|
|
||||||
}
|
|
||||||
COTISATIONS_CUSTOM = {
|
|
||||||
'invoice_date': ['date'],
|
|
||||||
'invoice_id': ['id'],
|
|
||||||
'invoice_recipient': ['recipient'],
|
|
||||||
'invoice_address': ['address'],
|
|
||||||
'invoice_payment': ['payment'],
|
|
||||||
'default': ['-date']
|
|
||||||
}
|
|
||||||
COTISATIONS_CONTROL = {
|
|
||||||
'control_name': ['user__adherent__name'],
|
|
||||||
'control_surname': ['user__surname'],
|
|
||||||
'control_paiement': ['paiement'],
|
|
||||||
'control_date': ['date'],
|
|
||||||
'control_valid': ['valid'],
|
|
||||||
'control_control': ['control'],
|
|
||||||
'control_id': ['id'],
|
|
||||||
'control_user-id': ['user__id'],
|
|
||||||
'default': ['-date']
|
|
||||||
}
|
|
||||||
TOPOLOGIE_INDEX = {
|
|
||||||
'switch_dns': ['interface__domain__name'],
|
|
||||||
'switch_ip': ['interface__ipv4__ipv4'],
|
|
||||||
'switch_loc': ['switchbay__name'],
|
|
||||||
'switch_ports': ['number'],
|
|
||||||
'switch_stack': ['stack__name'],
|
|
||||||
'default': ['switchbay', 'stack', 'stack_member_id']
|
|
||||||
}
|
|
||||||
TOPOLOGIE_INDEX_PORT = {
|
|
||||||
'port_port': ['port'],
|
|
||||||
'port_room': ['room__name'],
|
|
||||||
'port_interface': ['machine_interface__domain__name'],
|
|
||||||
'port_related': ['related__switch__name'],
|
|
||||||
'port_radius': ['radius'],
|
|
||||||
'port_vlan': ['vlan_force__name'],
|
|
||||||
'default': ['port']
|
|
||||||
}
|
|
||||||
TOPOLOGIE_INDEX_ROOM = {
|
|
||||||
'room_name': ['name'],
|
|
||||||
'default': ['name']
|
|
||||||
}
|
|
||||||
TOPOLOGIE_INDEX_BUILDING = {
|
|
||||||
'building_name': ['name'],
|
|
||||||
'default': ['name']
|
|
||||||
}
|
|
||||||
TOPOLOGIE_INDEX_BORNE = {
|
|
||||||
'ap_name': ['interface__domain__name'],
|
|
||||||
'ap_ip': ['interface__ipv4__ipv4'],
|
|
||||||
'ap_mac': ['interface__mac_address'],
|
|
||||||
'default': ['interface__domain__name']
|
|
||||||
}
|
|
||||||
TOPOLOGIE_INDEX_STACK = {
|
|
||||||
'stack_name': ['name'],
|
|
||||||
'stack_id': ['stack_id'],
|
|
||||||
'default': ['stack_id'],
|
|
||||||
}
|
|
||||||
TOPOLOGIE_INDEX_MODEL_SWITCH = {
|
|
||||||
'model-switch_name': ['reference'],
|
|
||||||
'model-switch_contructor': ['constructor__name'],
|
|
||||||
'default': ['reference'],
|
|
||||||
}
|
|
||||||
TOPOLOGIE_INDEX_SWITCH_BAY = {
|
|
||||||
'switch-bay_name': ['name'],
|
|
||||||
'switch-bay_building': ['building__name'],
|
|
||||||
'default': ['name'],
|
|
||||||
}
|
|
||||||
TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH = {
|
|
||||||
'constructor-switch_name': ['name'],
|
|
||||||
'default': ['name'],
|
|
||||||
}
|
|
||||||
LOGS_INDEX = {
|
|
||||||
'sum_date': ['revision__date_created'],
|
|
||||||
'default': ['-revision__date_created'],
|
|
||||||
}
|
|
||||||
LOGS_STATS_LOGS = {
|
|
||||||
'logs_author': ['user__name'],
|
|
||||||
'logs_date': ['date_created'],
|
|
||||||
'default': ['-date_created']
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def sort(request, col, order, values):
|
|
||||||
""" Check if the given values are possible and add .order_by() and
|
|
||||||
a .reverse() as specified according to those values """
|
|
||||||
fields = values.get(col, None)
|
|
||||||
if not fields:
|
|
||||||
fields = values.get('default', [])
|
|
||||||
request = request.order_by(*fields)
|
|
||||||
if values.get(col, None) and order == 'desc':
|
|
||||||
return request.reverse()
|
|
||||||
else:
|
|
||||||
return request
|
|
||||||
|
|
||||||
|
|
||||||
def re2o_paginator(request, query_set, pagination_number):
|
|
||||||
"""Paginator script for list display in re2o.
|
|
||||||
:request:
|
|
||||||
:query_set: Query_set to paginate
|
|
||||||
:pagination_number: Number of entries to display"""
|
|
||||||
paginator = Paginator(query_set, pagination_number)
|
|
||||||
page = request.GET.get('page')
|
|
||||||
try:
|
|
||||||
results = paginator.page(page)
|
|
||||||
except PageNotAnInteger:
|
|
||||||
# If page is not an integer, deliver first page.
|
|
||||||
results = paginator.page(1)
|
|
||||||
except EmptyPage:
|
|
||||||
# If page is out of range (e.g. 9999), deliver last page of results.
|
|
||||||
results = paginator.page(paginator.num_pages)
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
def remove_user_room(room):
|
def remove_user_room(room):
|
||||||
""" Déménage de force l'ancien locataire de la chambre """
|
""" Déménage de force l'ancien locataire de la chambre """
|
||||||
try:
|
try:
|
||||||
|
@ -370,18 +168,3 @@ def remove_user_room(room):
|
||||||
user.room = None
|
user.room = None
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
|
|
||||||
def get_input_formats_help_text(input_formats):
|
|
||||||
"""Returns a help text about the possible input formats"""
|
|
||||||
if len(input_formats) > 1:
|
|
||||||
help_text_template="Format: {main} {more}"
|
|
||||||
else:
|
|
||||||
help_text_template="Format: {main}"
|
|
||||||
more_text_template="<i class=\"fa fa-question-circle\" title=\"{}\"></i>"
|
|
||||||
help_text = help_text_template.format(
|
|
||||||
main=convert_datetime_format(input_formats[0]),
|
|
||||||
more=more_text_template.format(
|
|
||||||
'\n'.join(map(convert_datetime_format, input_formats))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return help_text
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ from __future__ import unicode_literals
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms import Form
|
from django.forms import Form
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from re2o.utils import get_input_formats_help_text
|
from re2o.base import get_input_formats_help_text
|
||||||
|
|
||||||
CHOICES_USER = (
|
CHOICES_USER = (
|
||||||
('0', _("Active")),
|
('0', _("Active")),
|
||||||
|
|
|
@ -46,7 +46,7 @@ from search.forms import (
|
||||||
CHOICES_AFF,
|
CHOICES_AFF,
|
||||||
initial_choices
|
initial_choices
|
||||||
)
|
)
|
||||||
from re2o.utils import SortTable
|
from re2o.base import SortTable
|
||||||
from re2o.acl import can_view_all
|
from re2o.acl import can_view_all
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,10 @@ from django.utils.translation import ugettext as _
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from users.views import form
|
from users.views import form
|
||||||
from re2o.utils import re2o_paginator, SortTable
|
from re2o.base import (
|
||||||
|
re2o_paginator,
|
||||||
|
SortTable,
|
||||||
|
)
|
||||||
from re2o.acl import (
|
from re2o.acl import (
|
||||||
can_create,
|
can_create,
|
||||||
can_edit,
|
can_edit,
|
||||||
|
|
|
@ -45,7 +45,8 @@ from django.utils.safestring import mark_safe
|
||||||
from machines.models import Interface, Machine, Nas
|
from machines.models import Interface, Machine, Nas
|
||||||
from topologie.models import Port
|
from topologie.models import Port
|
||||||
from preferences.models import OptionalUser
|
from preferences.models import OptionalUser
|
||||||
from re2o.utils import remove_user_room, get_input_formats_help_text
|
from re2o.utils import remove_user_room
|
||||||
|
from re2o.base import get_input_formats_help_text
|
||||||
from re2o.mixins import FormRevMixin
|
from re2o.mixins import FormRevMixin
|
||||||
from re2o.field_permissions import FieldPermissionFormMixin
|
from re2o.field_permissions import FieldPermissionFormMixin
|
||||||
|
|
||||||
|
@ -444,6 +445,7 @@ class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
|
||||||
'school',
|
'school',
|
||||||
'comment',
|
'comment',
|
||||||
'room',
|
'room',
|
||||||
|
'email',
|
||||||
'telephone',
|
'telephone',
|
||||||
'email',
|
'email',
|
||||||
'shell',
|
'shell',
|
||||||
|
|
|
@ -81,6 +81,7 @@ from re2o.settings import LDAP, GID_RANGES, UID_RANGES
|
||||||
from re2o.login import hashNT
|
from re2o.login import hashNT
|
||||||
from re2o.field_permissions import FieldPermissionModelMixin
|
from re2o.field_permissions import FieldPermissionModelMixin
|
||||||
from re2o.mixins import AclMixin, RevMixin
|
from re2o.mixins import AclMixin, RevMixin
|
||||||
|
from re2o.base import smtp_check
|
||||||
|
|
||||||
from cotisations.models import Cotisation, Facture, Paiement, Vente
|
from cotisations.models import Cotisation, Facture, Paiement, Vente
|
||||||
from machines.models import Domain, Interface, Machine, regen
|
from machines.models import Domain, Interface, Machine, regen
|
||||||
|
@ -576,7 +577,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
|
||||||
mac_refresh : synchronise les machines de l'user
|
mac_refresh : synchronise les machines de l'user
|
||||||
group_refresh : synchronise les group de l'user
|
group_refresh : synchronise les group de l'user
|
||||||
Si l'instance n'existe pas, on crée le ldapuser correspondant"""
|
Si l'instance n'existe pas, on crée le ldapuser correspondant"""
|
||||||
if sys.version_info[0] >= 3:
|
if sys.version_info[0] >= 3 and self.state != self.STATE_ARCHIVE and\
|
||||||
|
self.state != self.STATE_DISABLED:
|
||||||
self.refresh_from_db()
|
self.refresh_from_db()
|
||||||
try:
|
try:
|
||||||
user_ldap = LdapUser.objects.get(uidNumber=self.uid_number)
|
user_ldap = LdapUser.objects.get(uidNumber=self.uid_number)
|
||||||
|
@ -1889,6 +1891,9 @@ class EMailAddress(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
def clean(self, *args, **kwargs):
|
def clean(self, *args, **kwargs):
|
||||||
self.local_part = self.local_part.lower()
|
self.local_part = self.local_part.lower()
|
||||||
if "@" in self.local_part:
|
if "@" in self.local_part or "+" in self.local_part:
|
||||||
raise ValidationError(_("The local part must not contain @."))
|
raise ValidationError(_("The local part must not contain @ or +."))
|
||||||
|
result, reason = smtp_check(self.local_part)
|
||||||
|
if result:
|
||||||
|
raise ValidationError(reason)
|
||||||
super(EMailAddress, self).clean(*args, **kwargs)
|
super(EMailAddress, self).clean(*args, **kwargs)
|
||||||
|
|
|
@ -57,8 +57,10 @@ from preferences.models import OptionalUser, GeneralOption, AssoOption
|
||||||
from re2o.views import form
|
from re2o.views import form
|
||||||
from re2o.utils import (
|
from re2o.utils import (
|
||||||
all_has_access,
|
all_has_access,
|
||||||
SortTable,
|
)
|
||||||
re2o_paginator
|
from re2o.base import (
|
||||||
|
re2o_paginator,
|
||||||
|
SortTable
|
||||||
)
|
)
|
||||||
from re2o.acl import (
|
from re2o.acl import (
|
||||||
can_create,
|
can_create,
|
||||||
|
|
Loading…
Reference in a new issue