mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-08 19:06:25 +00:00
Merge branch 'moamoak/pep8_and_pylint' into 'master'
Pep8 and Pylint cleaning See merge request federez/re2o!127
This commit is contained in:
commit
d911452ae0
90 changed files with 4281 additions and 2155 deletions
|
@ -29,20 +29,20 @@ from machines.models import (
|
||||||
IpType,
|
IpType,
|
||||||
Extension,
|
Extension,
|
||||||
IpList,
|
IpList,
|
||||||
MachineType,
|
|
||||||
Domain,
|
Domain,
|
||||||
Txt,
|
Txt,
|
||||||
Mx,
|
Mx,
|
||||||
Srv,
|
Srv,
|
||||||
Service_link,
|
Service_link,
|
||||||
Ns,
|
Ns,
|
||||||
OuverturePortList,
|
|
||||||
OuverturePort,
|
OuverturePort,
|
||||||
Ipv6List
|
Ipv6List
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ServiceLinkSerializer(serializers.ModelSerializer):
|
class ServiceLinkSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serializer for the ServiceLink objects """
|
||||||
|
|
||||||
name = serializers.CharField(source='service.service_type')
|
name = serializers.CharField(source='service.service_type')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -51,6 +51,8 @@ class ServiceLinkSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class MailingSerializer(serializers.ModelSerializer):
|
class MailingSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serializer to build Mailing objects """
|
||||||
|
|
||||||
name = serializers.CharField(source='pseudo')
|
name = serializers.CharField(source='pseudo')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -59,20 +61,27 @@ class MailingSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class MailingMemberSerializer(serializers.ModelSerializer):
|
class MailingMemberSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serializer fot the Adherent objects (who belong to a
|
||||||
|
Mailing) """
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Adherent
|
model = Adherent
|
||||||
fields = ('email', 'name', 'surname', 'pseudo',)
|
fields = ('email',)
|
||||||
|
|
||||||
|
|
||||||
class IpTypeField(serializers.RelatedField):
|
class IpTypeField(serializers.RelatedField):
|
||||||
"""Serialisation d'une iptype, renvoie son evaluation str"""
|
""" Serializer for an IpType object field """
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return value.type
|
return value.type
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class IpListSerializer(serializers.ModelSerializer):
|
class IpListSerializer(serializers.ModelSerializer):
|
||||||
"""Serialisation d'une iplist, ip_type etant une foreign_key,
|
""" Serializer for an Ipv4List obejct using the IpType serialization """
|
||||||
on evalue sa methode str"""
|
|
||||||
ip_type = IpTypeField(read_only=True)
|
ip_type = IpTypeField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -81,16 +90,19 @@ class IpListSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class Ipv6ListSerializer(serializers.ModelSerializer):
|
class Ipv6ListSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serializer for an Ipv6List object """
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Ipv6List
|
model = Ipv6List
|
||||||
fields = ('ipv6', 'slaac_ip')
|
fields = ('ipv6', 'slaac_ip')
|
||||||
|
|
||||||
|
|
||||||
class InterfaceSerializer(serializers.ModelSerializer):
|
class InterfaceSerializer(serializers.ModelSerializer):
|
||||||
"""Serialisation d'une interface, ipv4, domain et extension sont
|
""" Serializer for an Interface object. Use SerializerMethodField
|
||||||
des foreign_key, on les override et on les evalue avec des fonctions
|
to get ForeignKey values """
|
||||||
get_..."""
|
|
||||||
ipv4 = IpListSerializer(read_only=True)
|
ipv4 = IpListSerializer(read_only=True)
|
||||||
|
# TODO : use serializer.RelatedField to avoid duplicate code
|
||||||
mac_address = serializers.SerializerMethodField('get_macaddress')
|
mac_address = serializers.SerializerMethodField('get_macaddress')
|
||||||
domain = serializers.SerializerMethodField('get_dns')
|
domain = serializers.SerializerMethodField('get_dns')
|
||||||
extension = serializers.SerializerMethodField('get_interface_extension')
|
extension = serializers.SerializerMethodField('get_interface_extension')
|
||||||
|
@ -99,20 +111,29 @@ class InterfaceSerializer(serializers.ModelSerializer):
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = ('ipv4', 'mac_address', 'domain', 'extension')
|
fields = ('ipv4', 'mac_address', 'domain', 'extension')
|
||||||
|
|
||||||
def get_dns(self, obj):
|
@staticmethod
|
||||||
|
def get_dns(obj):
|
||||||
|
""" The name of the associated DNS object """
|
||||||
return obj.domain.name
|
return obj.domain.name
|
||||||
|
|
||||||
def get_interface_extension(self, obj):
|
@staticmethod
|
||||||
|
def get_interface_extension(obj):
|
||||||
|
""" The name of the associated Interface object """
|
||||||
return obj.domain.extension.name
|
return obj.domain.extension.name
|
||||||
|
|
||||||
def get_macaddress(self, obj):
|
@staticmethod
|
||||||
|
def get_macaddress(obj):
|
||||||
|
""" The string representation of the associated MAC address """
|
||||||
return str(obj.mac_address)
|
return str(obj.mac_address)
|
||||||
|
|
||||||
|
|
||||||
class FullInterfaceSerializer(serializers.ModelSerializer):
|
class FullInterfaceSerializer(serializers.ModelSerializer):
|
||||||
"""Serialisation complete d'une interface avec les ipv6 en plus"""
|
""" Serializer for an Interface obejct. Use SerializerMethodField
|
||||||
|
to get ForeignKey values """
|
||||||
|
|
||||||
ipv4 = IpListSerializer(read_only=True)
|
ipv4 = IpListSerializer(read_only=True)
|
||||||
ipv6 = Ipv6ListSerializer(read_only=True, many=True)
|
ipv6 = Ipv6ListSerializer(read_only=True, many=True)
|
||||||
|
# TODO : use serializer.RelatedField to avoid duplicate code
|
||||||
mac_address = serializers.SerializerMethodField('get_macaddress')
|
mac_address = serializers.SerializerMethodField('get_macaddress')
|
||||||
domain = serializers.SerializerMethodField('get_dns')
|
domain = serializers.SerializerMethodField('get_dns')
|
||||||
extension = serializers.SerializerMethodField('get_interface_extension')
|
extension = serializers.SerializerMethodField('get_interface_extension')
|
||||||
|
@ -121,26 +142,36 @@ class FullInterfaceSerializer(serializers.ModelSerializer):
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = ('ipv4', 'ipv6', 'mac_address', 'domain', 'extension')
|
fields = ('ipv4', 'ipv6', 'mac_address', 'domain', 'extension')
|
||||||
|
|
||||||
def get_dns(self, obj):
|
@staticmethod
|
||||||
|
def get_dns(obj):
|
||||||
|
""" The name of the associated DNS object """
|
||||||
return obj.domain.name
|
return obj.domain.name
|
||||||
|
|
||||||
def get_interface_extension(self, obj):
|
@staticmethod
|
||||||
|
def get_interface_extension(obj):
|
||||||
|
""" The name of the associated Extension object """
|
||||||
return obj.domain.extension.name
|
return obj.domain.extension.name
|
||||||
|
|
||||||
def get_macaddress(self, obj):
|
@staticmethod
|
||||||
|
def get_macaddress(obj):
|
||||||
|
""" The string representation of the associated MAC address """
|
||||||
return str(obj.mac_address)
|
return str(obj.mac_address)
|
||||||
|
|
||||||
|
|
||||||
class ExtensionNameField(serializers.RelatedField):
|
class ExtensionNameField(serializers.RelatedField):
|
||||||
"""Evaluation str d'un objet extension (.example.org)"""
|
""" Serializer for Extension object field """
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return value.name
|
return value.name
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TypeSerializer(serializers.ModelSerializer):
|
class TypeSerializer(serializers.ModelSerializer):
|
||||||
"""Serialisation d'un iptype : extension et la liste des
|
""" Serializer for an IpType object. Use SerializerMethodField to
|
||||||
ouvertures de port son evalués en get_... etant des
|
get ForeignKey values """
|
||||||
foreign_key ou des relations manytomany"""
|
|
||||||
extension = ExtensionNameField(read_only=True)
|
extension = ExtensionNameField(read_only=True)
|
||||||
ouverture_ports_tcp_in = serializers\
|
ouverture_ports_tcp_in = serializers\
|
||||||
.SerializerMethodField('get_port_policy_input_tcp')
|
.SerializerMethodField('get_port_policy_input_tcp')
|
||||||
|
@ -158,7 +189,10 @@ class TypeSerializer(serializers.ModelSerializer):
|
||||||
'ouverture_ports_tcp_in', 'ouverture_ports_tcp_out',
|
'ouverture_ports_tcp_in', 'ouverture_ports_tcp_out',
|
||||||
'ouverture_ports_udp_in', 'ouverture_ports_udp_out',)
|
'ouverture_ports_udp_in', 'ouverture_ports_udp_out',)
|
||||||
|
|
||||||
def get_port_policy(self, obj, protocole, io):
|
@staticmethod
|
||||||
|
def get_port_policy(obj, protocole, io):
|
||||||
|
""" Generic utility function to get the policy for a given
|
||||||
|
port, protocole and IN or OUT """
|
||||||
if obj.ouverture_ports is None:
|
if obj.ouverture_ports is None:
|
||||||
return []
|
return []
|
||||||
return map(
|
return map(
|
||||||
|
@ -196,14 +230,20 @@ class ExtensionSerializer(serializers.ModelSerializer):
|
||||||
model = Extension
|
model = Extension
|
||||||
fields = ('name', 'origin', 'origin_v6', 'zone_entry', 'soa')
|
fields = ('name', 'origin', 'origin_v6', 'zone_entry', 'soa')
|
||||||
|
|
||||||
def get_origin_ip(self, obj):
|
@staticmethod
|
||||||
|
def get_origin_ip(obj):
|
||||||
|
""" The IP of the associated origin for the zone """
|
||||||
return obj.origin.ipv4
|
return obj.origin.ipv4
|
||||||
|
|
||||||
def get_zone_name(self, obj):
|
@staticmethod
|
||||||
|
def get_zone_name(obj):
|
||||||
|
""" The name of the associated zone """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
def get_soa_data(self, obj):
|
@staticmethod
|
||||||
return { 'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param }
|
def get_soa_data(obj):
|
||||||
|
""" The representation of the associated SOA """
|
||||||
|
return {'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param}
|
||||||
|
|
||||||
|
|
||||||
class MxSerializer(serializers.ModelSerializer):
|
class MxSerializer(serializers.ModelSerializer):
|
||||||
|
@ -217,13 +257,19 @@ class MxSerializer(serializers.ModelSerializer):
|
||||||
model = Mx
|
model = Mx
|
||||||
fields = ('zone', 'priority', 'name', 'mx_entry')
|
fields = ('zone', 'priority', 'name', 'mx_entry')
|
||||||
|
|
||||||
def get_entry_name(self, obj):
|
@staticmethod
|
||||||
|
def get_entry_name(obj):
|
||||||
|
""" The name of the DNS MX entry """
|
||||||
return str(obj.name)
|
return str(obj.name)
|
||||||
|
|
||||||
def get_zone_name(self, obj):
|
@staticmethod
|
||||||
|
def get_zone_name(obj):
|
||||||
|
""" The name of the associated zone of the MX record """
|
||||||
return obj.zone.name
|
return obj.zone.name
|
||||||
|
|
||||||
def get_mx_name(self, obj):
|
@staticmethod
|
||||||
|
def get_mx_name(obj):
|
||||||
|
""" The string representation of the entry to add to the DNS """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@ -237,10 +283,14 @@ class TxtSerializer(serializers.ModelSerializer):
|
||||||
model = Txt
|
model = Txt
|
||||||
fields = ('zone', 'txt_entry', 'field1', 'field2')
|
fields = ('zone', 'txt_entry', 'field1', 'field2')
|
||||||
|
|
||||||
def get_zone_name(self, obj):
|
@staticmethod
|
||||||
|
def get_zone_name(obj):
|
||||||
|
""" The name of the associated zone """
|
||||||
return str(obj.zone.name)
|
return str(obj.zone.name)
|
||||||
|
|
||||||
def get_txt_name(self, obj):
|
@staticmethod
|
||||||
|
def get_txt_name(obj):
|
||||||
|
""" The string representation of the entry to add to the DNS """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@ -263,10 +313,14 @@ class SrvSerializer(serializers.ModelSerializer):
|
||||||
'srv_entry'
|
'srv_entry'
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_extension_name(self, obj):
|
@staticmethod
|
||||||
|
def get_extension_name(obj):
|
||||||
|
""" The name of the associated extension """
|
||||||
return str(obj.extension.name)
|
return str(obj.extension.name)
|
||||||
|
|
||||||
def get_srv_name(self, obj):
|
@staticmethod
|
||||||
|
def get_srv_name(obj):
|
||||||
|
""" The string representation of the entry to add to the DNS """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@ -281,13 +335,19 @@ class NsSerializer(serializers.ModelSerializer):
|
||||||
model = Ns
|
model = Ns
|
||||||
fields = ('zone', 'ns', 'ns_entry')
|
fields = ('zone', 'ns', 'ns_entry')
|
||||||
|
|
||||||
def get_zone_name(self, obj):
|
@staticmethod
|
||||||
|
def get_zone_name(obj):
|
||||||
|
""" The name of the associated zone """
|
||||||
return obj.zone.name
|
return obj.zone.name
|
||||||
|
|
||||||
def get_domain_name(self, obj):
|
@staticmethod
|
||||||
|
def get_domain_name(obj):
|
||||||
|
""" The name of the associated NS target """
|
||||||
return str(obj.ns)
|
return str(obj.ns)
|
||||||
|
|
||||||
def get_text_name(self, obj):
|
@staticmethod
|
||||||
|
def get_text_name(obj):
|
||||||
|
""" The string representation of the entry to add to the DNS """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@ -302,13 +362,19 @@ class DomainSerializer(serializers.ModelSerializer):
|
||||||
model = Domain
|
model = Domain
|
||||||
fields = ('name', 'extension', 'cname', 'cname_entry')
|
fields = ('name', 'extension', 'cname', 'cname_entry')
|
||||||
|
|
||||||
def get_zone_name(self, obj):
|
@staticmethod
|
||||||
|
def get_zone_name(obj):
|
||||||
|
""" The name of the associated zone """
|
||||||
return obj.extension.name
|
return obj.extension.name
|
||||||
|
|
||||||
def get_alias_name(self, obj):
|
@staticmethod
|
||||||
|
def get_alias_name(obj):
|
||||||
|
""" The name of the associated alias """
|
||||||
return str(obj.cname)
|
return str(obj.cname)
|
||||||
|
|
||||||
def get_cname_name(self, obj):
|
@staticmethod
|
||||||
|
def get_cname_name(obj):
|
||||||
|
""" The name of the associated CNAME target """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@ -322,13 +388,19 @@ class ServicesSerializer(serializers.ModelSerializer):
|
||||||
model = Service_link
|
model = Service_link
|
||||||
fields = ('server', 'service', 'need_regen')
|
fields = ('server', 'service', 'need_regen')
|
||||||
|
|
||||||
def get_server_name(self, obj):
|
@staticmethod
|
||||||
|
def get_server_name(obj):
|
||||||
|
""" The name of the associated server """
|
||||||
return str(obj.server.domain.name)
|
return str(obj.server.domain.name)
|
||||||
|
|
||||||
def get_service_name(self, obj):
|
@staticmethod
|
||||||
|
def get_service_name(obj):
|
||||||
|
""" The name of the service name """
|
||||||
return str(obj.service)
|
return str(obj.service)
|
||||||
|
|
||||||
def get_regen_status(self, obj):
|
@staticmethod
|
||||||
|
def get_regen_status(obj):
|
||||||
|
""" The string representation of the regen status """
|
||||||
return obj.need_regen()
|
return obj.need_regen()
|
||||||
|
|
||||||
|
|
||||||
|
@ -337,24 +409,38 @@ class OuverturePortsSerializer(serializers.Serializer):
|
||||||
ipv4 = serializers.SerializerMethodField()
|
ipv4 = serializers.SerializerMethodField()
|
||||||
ipv6 = serializers.SerializerMethodField()
|
ipv6 = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
""" Creates a new object based on the un-serialized data.
|
||||||
|
Used to implement an abstract inherited method """
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
""" Updates an object based on the un-serialized data.
|
||||||
|
Used to implement an abstract inherited method """
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def get_ipv4():
|
def get_ipv4():
|
||||||
return {i.ipv4.ipv4:
|
""" The representation of the policy for the IPv4 addresses """
|
||||||
{
|
return {
|
||||||
"tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()],
|
i.ipv4.ipv4: {
|
||||||
"tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()],
|
"tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()],
|
||||||
"udp_in":[j.udp_ports_in() for j in i.port_lists.all()],
|
"tcp_out": [j.tcp_ports_out()for j in i.port_lists.all()],
|
||||||
"udp_out":[j.udp_ports_out() for j in i.port_lists.all()],
|
"udp_in": [j.udp_ports_in() for j in i.port_lists.all()],
|
||||||
|
"udp_out": [j.udp_ports_out() for j in i.port_lists.all()],
|
||||||
}
|
}
|
||||||
for i in Interface.objects.all() if i.ipv4
|
for i in Interface.objects.all() if i.ipv4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def get_ipv6():
|
def get_ipv6():
|
||||||
return {i.ipv6:
|
""" The representation of the policy for the IPv6 addresses """
|
||||||
{
|
return {
|
||||||
"tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()],
|
i.ipv6: {
|
||||||
"tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()],
|
"tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()],
|
||||||
"udp_in":[j.udp_ports_in() for j in i.port_lists.all()],
|
"tcp_out": [j.tcp_ports_out()for j in i.port_lists.all()],
|
||||||
"udp_out":[j.udp_ports_out() for j in i.port_lists.all()],
|
"udp_in": [j.udp_ports_in() for j in i.port_lists.all()],
|
||||||
|
"udp_out": [j.udp_ports_out() for j in i.port_lists.all()],
|
||||||
}
|
}
|
||||||
for i in Interface.objects.all() if i.ipv6
|
for i in Interface.objects.all() if i.ipv6
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
# 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.
|
||||||
|
"""api.tests
|
||||||
|
The tests for the API module.
|
||||||
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
# from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
15
api/urls.py
15
api/urls.py
|
@ -32,7 +32,10 @@ from . import views
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Services
|
# Services
|
||||||
url(r'^services/$', views.services),
|
url(r'^services/$', views.services),
|
||||||
url(r'^services/(?P<server_name>\w+)/(?P<service_name>\w+)/regen/$', views.services_server_service_regen),
|
url(
|
||||||
|
r'^services/(?P<server_name>\w+)/(?P<service_name>\w+)/regen/$',
|
||||||
|
views.services_server_service_regen
|
||||||
|
),
|
||||||
url(r'^services/(?P<server_name>\w+)/$', views.services_server),
|
url(r'^services/(?P<server_name>\w+)/$', views.services_server),
|
||||||
|
|
||||||
# DNS
|
# DNS
|
||||||
|
@ -56,7 +59,13 @@ urlpatterns = [
|
||||||
|
|
||||||
# Mailings
|
# Mailings
|
||||||
url(r'^mailing/standard/$', views.mailing_standard),
|
url(r'^mailing/standard/$', views.mailing_standard),
|
||||||
url(r'^mailing/standard/(?P<ml_name>\w+)/members/$', views.mailing_standard_ml_members),
|
url(
|
||||||
|
r'^mailing/standard/(?P<ml_name>\w+)/members/$',
|
||||||
|
views.mailing_standard_ml_members
|
||||||
|
),
|
||||||
url(r'^mailing/club/$', views.mailing_club),
|
url(r'^mailing/club/$', views.mailing_club),
|
||||||
url(r'^mailing/club/(?P<ml_name>\w+)/members/$', views.mailing_club_ml_members),
|
url(
|
||||||
|
r'^mailing/club/(?P<ml_name>\w+)/members/$',
|
||||||
|
views.mailing_club_ml_members
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
45
api/utils.py
45
api/utils.py
|
@ -26,6 +26,7 @@ Set of various and usefull functions for the API app
|
||||||
from rest_framework.renderers import JSONRenderer
|
from rest_framework.renderers import JSONRenderer
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
|
|
||||||
class JSONResponse(HttpResponse):
|
class JSONResponse(HttpResponse):
|
||||||
"""A JSON response that can be send as an HTTP response.
|
"""A JSON response that can be send as an HTTP response.
|
||||||
Usefull in case of REST API.
|
Usefull in case of REST API.
|
||||||
|
@ -51,23 +52,23 @@ class JSONResponse(HttpResponse):
|
||||||
class JSONError(JSONResponse):
|
class JSONError(JSONResponse):
|
||||||
"""A JSON response when the request failed.
|
"""A JSON response when the request failed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, error_msg, data=None, **kwargs):
|
def __init__(self, error_msg, data=None, **kwargs):
|
||||||
"""Initialise a JSONError object.
|
"""Initialise a JSONError object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
error_msg: A message explaining where the error is.
|
error_msg: A message explaining where the error is.
|
||||||
data: An optional field for further data to send along.
|
data: An optional field for further data to send along.
|
||||||
|
|
||||||
Creates:
|
Creates:
|
||||||
A JSONResponse containing a field `status` set to `error` and a field
|
A JSONResponse containing a field `status` set to `error` and a
|
||||||
`reason` containing `error_msg`. If `data` argument has been given,
|
field `reason` containing `error_msg`. If `data` argument has been
|
||||||
a field `data` containing it is added to the JSON response.
|
given, a field `data` containing it is added to the JSON response.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
'status' : 'error',
|
'status': 'error',
|
||||||
'reason' : error_msg
|
'reason': error_msg
|
||||||
}
|
}
|
||||||
if data is not None:
|
if data is not None:
|
||||||
response['data'] = data
|
response['data'] = data
|
||||||
|
@ -77,22 +78,22 @@ class JSONError(JSONResponse):
|
||||||
class JSONSuccess(JSONResponse):
|
class JSONSuccess(JSONResponse):
|
||||||
"""A JSON response when the request suceeded.
|
"""A JSON response when the request suceeded.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, data=None, **kwargs):
|
def __init__(self, data=None, **kwargs):
|
||||||
"""Initialise a JSONSucess object.
|
"""Initialise a JSONSucess object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
error_msg: A message explaining where the error is.
|
error_msg: A message explaining where the error is.
|
||||||
data: An optional field for further data to send along.
|
data: An optional field for further data to send along.
|
||||||
|
|
||||||
Creates:
|
Creates:
|
||||||
A JSONResponse containing a field `status` set to `sucess`. If `data`
|
A JSONResponse containing a field `status` set to `sucess`. If
|
||||||
argument has been given, a field `data` containing it is added to the
|
`data` argument has been given, a field `data` containing it is
|
||||||
JSON response.
|
added to the JSON response.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
response = {
|
response = {
|
||||||
'status' : 'success',
|
'status': 'success',
|
||||||
}
|
}
|
||||||
if data is not None:
|
if data is not None:
|
||||||
response['data'] = data
|
response['data'] = data
|
||||||
|
@ -103,12 +104,20 @@ def accept_method(methods):
|
||||||
"""Decorator to set a list of accepted request method.
|
"""Decorator to set a list of accepted request method.
|
||||||
Check if the method used is accepted. If not, send a NotAllowed response.
|
Check if the method used is accepted. If not, send a NotAllowed response.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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
|
||||||
|
"""
|
||||||
if request.method in methods:
|
if request.method in methods:
|
||||||
return view(request, *args, **kwargs)
|
return view(request, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
return JSONError('Invalid request method. Request methods authorize are '+str(methods))
|
return JSONError(
|
||||||
|
'Invalid request method. Request methods authorize are ' +
|
||||||
|
str(methods)
|
||||||
|
)
|
||||||
return view(request, *args, **kwargs)
|
return view(request, *args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
|
|
251
api/views.py
251
api/views.py
|
@ -27,13 +27,42 @@ HTML pages such as the login and index pages for a better integration.
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from re2o.utils import all_has_access, all_active_assigned_interfaces
|
from re2o.utils import (
|
||||||
|
all_has_access,
|
||||||
|
all_active_assigned_interfaces,
|
||||||
|
filter_active_interfaces
|
||||||
|
)
|
||||||
from users.models import Club
|
from users.models import Club
|
||||||
from machines.models import (Service_link, Service, Interface, Domain,
|
from machines.models import (
|
||||||
OuverturePortList)
|
Service_link,
|
||||||
|
Service,
|
||||||
|
Interface,
|
||||||
|
Domain,
|
||||||
|
IpType,
|
||||||
|
Mx,
|
||||||
|
Ns,
|
||||||
|
Txt,
|
||||||
|
Srv,
|
||||||
|
Extension,
|
||||||
|
OuverturePortList,
|
||||||
|
OuverturePort
|
||||||
|
)
|
||||||
|
|
||||||
from .serializers import *
|
from .serializers import (
|
||||||
|
ServicesSerializer,
|
||||||
|
ServiceLinkSerializer,
|
||||||
|
FullInterfaceSerializer,
|
||||||
|
DomainSerializer,
|
||||||
|
TypeSerializer,
|
||||||
|
MxSerializer,
|
||||||
|
NsSerializer,
|
||||||
|
TxtSerializer,
|
||||||
|
SrvSerializer,
|
||||||
|
ExtensionSerializer,
|
||||||
|
InterfaceSerializer,
|
||||||
|
MailingMemberSerializer,
|
||||||
|
MailingSerializer
|
||||||
|
)
|
||||||
from .utils import JSONError, JSONSuccess, accept_method
|
from .utils import JSONError, JSONSuccess, accept_method
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,21 +70,26 @@ from .utils import JSONError, JSONSuccess, accept_method
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def services(request):
|
def services(_request):
|
||||||
"""The list of the different services and servers couples
|
"""The list of the different services and servers couples
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
GET:
|
GET:
|
||||||
A JSONSuccess response with a field `data` containing:
|
A JSONSuccess response with a field `data` containing:
|
||||||
* a list of dictionnaries (one for each service-server couple) containing:
|
* a list of dictionnaries (one for each service-server couple)
|
||||||
|
containing:
|
||||||
* a field `server`: the server name
|
* a field `server`: the server name
|
||||||
* a field `service`: the service name
|
* a field `service`: the service name
|
||||||
* a field `need_regen`: does the service need a regeneration ?
|
* a field `need_regen`: does the service need a regeneration ?
|
||||||
"""
|
"""
|
||||||
service_link = Service_link.objects.all().select_related('server__domain').select_related('service')
|
|
||||||
|
service_link = (Service_link.objects.all()
|
||||||
|
.select_related('server__domain')
|
||||||
|
.select_related('service'))
|
||||||
seria = ServicesSerializer(service_link, many=True)
|
seria = ServicesSerializer(service_link, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
|
@ -72,6 +106,7 @@ def services_server_service_regen(request, server_name, service_name):
|
||||||
POST:
|
POST:
|
||||||
An empty JSONSuccess response.
|
An empty JSONSuccess response.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
query = Service_link.objects.filter(
|
query = Service_link.objects.filter(
|
||||||
service__in=Service.objects.filter(service_type=service_name),
|
service__in=Service.objects.filter(service_type=service_name),
|
||||||
server__in=Interface.objects.filter(
|
server__in=Interface.objects.filter(
|
||||||
|
@ -80,7 +115,7 @@ def services_server_service_regen(request, server_name, service_name):
|
||||||
)
|
)
|
||||||
if not query:
|
if not query:
|
||||||
return JSONError("This service is not active for this server")
|
return JSONError("This service is not active for this server")
|
||||||
|
|
||||||
service = query.first()
|
service = query.first()
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return JSONSuccess({'need_regen': service.need_regen()})
|
return JSONSuccess({'need_regen': service.need_regen()})
|
||||||
|
@ -93,7 +128,7 @@ def services_server_service_regen(request, server_name, service_name):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def services_server(request, server_name):
|
def services_server(_request, server_name):
|
||||||
"""The list of services attached to a specific server
|
"""The list of services attached to a specific server
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -102,6 +137,7 @@ def services_server(request, server_name):
|
||||||
* a list of dictionnaries (one for each service) containing:
|
* a list of dictionnaries (one for each service) containing:
|
||||||
* a field `name`: the name of a service
|
* a field `name`: the name of a service
|
||||||
"""
|
"""
|
||||||
|
|
||||||
query = Service_link.objects.filter(
|
query = Service_link.objects.filter(
|
||||||
server__in=Interface.objects.filter(
|
server__in=Interface.objects.filter(
|
||||||
domain__in=Domain.objects.filter(name=server_name)
|
domain__in=Domain.objects.filter(name=server_name)
|
||||||
|
@ -109,9 +145,9 @@ def services_server(request, server_name):
|
||||||
)
|
)
|
||||||
if not query:
|
if not query:
|
||||||
return JSONError("This service is not active for this server")
|
return JSONError("This service is not active for this server")
|
||||||
|
|
||||||
services = query.all()
|
services_objects = query.all()
|
||||||
seria = ServiceLinkSerializer(services, many=True)
|
seria = ServiceLinkSerializer(services_objects, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
|
||||||
|
|
||||||
|
@ -119,7 +155,7 @@ def services_server(request, server_name):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def dns_mac_ip_dns(request):
|
def dns_mac_ip_dns(_request):
|
||||||
"""The list of all active interfaces with all the associated infos
|
"""The list of all active interfaces with all the associated infos
|
||||||
(MAC, IP, IpType, DNS name and associated zone extension)
|
(MAC, IP, IpType, DNS name and associated zone extension)
|
||||||
|
|
||||||
|
@ -135,8 +171,10 @@ def dns_mac_ip_dns(request):
|
||||||
* a field `ip_type`: the name of the IpType of this interface
|
* a field `ip_type`: the name of the IpType of this interface
|
||||||
* a field `mac_address`: the MAC of this interface
|
* a field `mac_address`: the MAC of this interface
|
||||||
* a field `domain`: the DNS name for this interface
|
* a field `domain`: the DNS name for this interface
|
||||||
* a field `extension`: the extension for the DNS zone of this interface
|
* a field `extension`: the extension for the DNS zone of this
|
||||||
|
interface
|
||||||
"""
|
"""
|
||||||
|
|
||||||
interfaces = all_active_assigned_interfaces(full=True)
|
interfaces = all_active_assigned_interfaces(full=True)
|
||||||
seria = FullInterfaceSerializer(interfaces, many=True)
|
seria = FullInterfaceSerializer(interfaces, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
@ -146,7 +184,7 @@ def dns_mac_ip_dns(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def dns_alias(request):
|
def dns_alias(_request):
|
||||||
"""The list of all the alias used and the DNS info associated
|
"""The list of all the alias used and the DNS info associated
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -154,11 +192,23 @@ def dns_alias(request):
|
||||||
A JSON Success response with a field `data` containing:
|
A JSON Success response with a field `data` containing:
|
||||||
* a list of dictionnaries (one for each alias) containing:
|
* a list of dictionnaries (one for each alias) containing:
|
||||||
* a field `name`: the alias used
|
* a field `name`: the alias used
|
||||||
* a field `cname`: the target of the alias (real name of the interface)
|
* a field `cname`: the target of the alias (real name of the
|
||||||
* a field `cname_entry`: the entry to write in the DNS to have the alias
|
interface)
|
||||||
* a field `extension`: the extension for the DNS zone of this interface
|
* a field `cname_entry`: the entry to write in the DNS to have
|
||||||
|
the alias
|
||||||
|
* a field `extension`: the extension for the DNS zone of this
|
||||||
|
interface
|
||||||
"""
|
"""
|
||||||
alias = Domain.objects.filter(interface_parent=None).filter(cname__in=Domain.objects.filter(interface_parent__in=Interface.objects.exclude(ipv4=None))).select_related('extension').select_related('cname__extension')
|
|
||||||
|
alias = (Domain.objects
|
||||||
|
.filter(interface_parent=None)
|
||||||
|
.filter(
|
||||||
|
cname__in=Domain.objects.filter(
|
||||||
|
interface_parent__in=Interface.objects.exclude(ipv4=None)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.select_related('extension')
|
||||||
|
.select_related('cname__extension'))
|
||||||
seria = DomainSerializer(alias, many=True)
|
seria = DomainSerializer(alias, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
|
||||||
|
@ -167,7 +217,7 @@ def dns_alias(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def accesspoint_ip_dns(request):
|
def accesspoint_ip_dns(_request):
|
||||||
"""The list of all active interfaces with all the associated infos
|
"""The list of all active interfaces with all the associated infos
|
||||||
(MAC, IP, IpType, DNS name and associated zone extension)
|
(MAC, IP, IpType, DNS name and associated zone extension)
|
||||||
|
|
||||||
|
@ -185,10 +235,12 @@ def accesspoint_ip_dns(request):
|
||||||
* a field `ip_type`: the name of the IpType of this interface
|
* a field `ip_type`: the name of the IpType of this interface
|
||||||
* a field `mac_address`: the MAC of this interface
|
* a field `mac_address`: the MAC of this interface
|
||||||
* a field `domain`: the DNS name for this interface
|
* a field `domain`: the DNS name for this interface
|
||||||
* a field `extension`: the extension for the DNS zone of this interface
|
* a field `extension`: the extension for the DNS zone of this
|
||||||
|
interface
|
||||||
"""
|
"""
|
||||||
interfaces = all_active_assigned_interfaces(full=True)\
|
|
||||||
.filter(machine__accesspoint__isnull=False)
|
interfaces = (all_active_assigned_interfaces(full=True)
|
||||||
|
.filter(machine__accesspoint__isnull=False))
|
||||||
seria = FullInterfaceSerializer(interfaces, many=True)
|
seria = FullInterfaceSerializer(interfaces, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
|
||||||
|
@ -197,7 +249,7 @@ def accesspoint_ip_dns(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def dns_corresp(request):
|
def dns_corresp(_request):
|
||||||
"""The list of the IpTypes possible with the infos about each
|
"""The list of the IpTypes possible with the infos about each
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -208,12 +260,14 @@ def dns_corresp(request):
|
||||||
* a field `extension`: the DNS extension associated
|
* a field `extension`: the DNS extension associated
|
||||||
* a field `domain_ip_start`: the first ip to use for this type
|
* a field `domain_ip_start`: the first ip to use for this type
|
||||||
* a field `domain_ip_stop`: the last ip to use for this type
|
* a field `domain_ip_stop`: the last ip to use for this type
|
||||||
* a field `prefix_v6`: `null` if IPv6 is deactivated else the prefix to use
|
* a field `prefix_v6`: `null` if IPv6 is deactivated else the
|
||||||
|
prefix to use
|
||||||
* a field `ouverture_ports_tcp_in`: the policy for TCP IN ports
|
* a field `ouverture_ports_tcp_in`: the policy for TCP IN ports
|
||||||
* a field `ouverture_ports_tcp_out`: the policy for TCP OUT ports
|
* a field `ouverture_ports_tcp_out`: the policy for TCP OUT ports
|
||||||
* a field `ouverture_ports_udp_in`: the policy for UDP IN ports
|
* a field `ouverture_ports_udp_in`: the policy for UDP IN ports
|
||||||
* a field `ouverture_ports_udp_out`: the policy for UDP OUT ports
|
* a field `ouverture_ports_udp_out`: the policy for UDP OUT ports
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ip_type = IpType.objects.all().select_related('extension')
|
ip_type = IpType.objects.all().select_related('extension')
|
||||||
seria = TypeSerializer(ip_type, many=True)
|
seria = TypeSerializer(ip_type, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
@ -223,7 +277,7 @@ def dns_corresp(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def dns_mx(request):
|
def dns_mx(_request):
|
||||||
"""The list of MX record to add to the DNS
|
"""The list of MX record to add to the DNS
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -233,9 +287,13 @@ def dns_mx(request):
|
||||||
* a field `zone`: the extension for the concerned zone
|
* a field `zone`: the extension for the concerned zone
|
||||||
* a field `priority`: the priority to use
|
* a field `priority`: the priority to use
|
||||||
* a field `name`: the name of the target
|
* a field `name`: the name of the target
|
||||||
* a field `mx_entry`: the full entry to add in the DNS for this MX record
|
* a field `mx_entry`: the full entry to add in the DNS for this
|
||||||
|
MX record
|
||||||
"""
|
"""
|
||||||
mx = Mx.objects.all().select_related('zone').select_related('name__extension')
|
|
||||||
|
mx = (Mx.objects.all()
|
||||||
|
.select_related('zone')
|
||||||
|
.select_related('name__extension'))
|
||||||
seria = MxSerializer(mx, many=True)
|
seria = MxSerializer(mx, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
|
||||||
|
@ -244,7 +302,7 @@ def dns_mx(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def dns_ns(request):
|
def dns_ns(_request):
|
||||||
"""The list of NS record to add to the DNS
|
"""The list of NS record to add to the DNS
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -253,9 +311,18 @@ def dns_ns(request):
|
||||||
* a list of dictionnaries (one for each NS record) containing:
|
* a list of dictionnaries (one for each NS record) containing:
|
||||||
* a field `zone`: the extension for the concerned zone
|
* a field `zone`: the extension for the concerned zone
|
||||||
* a field `ns`: the DNS name for the NS server targeted
|
* a field `ns`: the DNS name for the NS server targeted
|
||||||
* a field `ns_entry`: the full entry to add in the DNS for this NS record
|
* a field `ns_entry`: the full entry to add in the DNS for this
|
||||||
|
NS record
|
||||||
"""
|
"""
|
||||||
ns = Ns.objects.exclude(ns__in=Domain.objects.filter(interface_parent__in=Interface.objects.filter(ipv4=None))).select_related('zone').select_related('ns__extension')
|
|
||||||
|
ns = (Ns.objects
|
||||||
|
.exclude(
|
||||||
|
ns__in=Domain.objects.filter(
|
||||||
|
interface_parent__in=Interface.objects.filter(ipv4=None)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.select_related('zone')
|
||||||
|
.select_related('ns__extension'))
|
||||||
seria = NsSerializer(ns, many=True)
|
seria = NsSerializer(ns, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
|
||||||
|
@ -264,7 +331,7 @@ def dns_ns(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def dns_txt(request):
|
def dns_txt(_request):
|
||||||
"""The list of TXT record to add to the DNS
|
"""The list of TXT record to add to the DNS
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -274,8 +341,10 @@ def dns_txt(request):
|
||||||
* a field `zone`: the extension for the concerned zone
|
* a field `zone`: the extension for the concerned zone
|
||||||
* a field `field1`: the first field in the record (target)
|
* a field `field1`: the first field in the record (target)
|
||||||
* a field `field2`: the second field in the record (value)
|
* a field `field2`: the second field in the record (value)
|
||||||
* a field `txt_entry`: the full entry to add in the DNS for this TXT record
|
* a field `txt_entry`: the full entry to add in the DNS for this
|
||||||
|
TXT record
|
||||||
"""
|
"""
|
||||||
|
|
||||||
txt = Txt.objects.all().select_related('zone')
|
txt = Txt.objects.all().select_related('zone')
|
||||||
seria = TxtSerializer(txt, many=True)
|
seria = TxtSerializer(txt, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
@ -285,7 +354,7 @@ def dns_txt(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def dns_srv(request):
|
def dns_srv(_request):
|
||||||
"""The list of SRV record to add to the DNS
|
"""The list of SRV record to add to the DNS
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -300,9 +369,13 @@ def dns_srv(request):
|
||||||
* a field `weight`: the weight for same priority entries
|
* a field `weight`: the weight for same priority entries
|
||||||
* a field `port`: the port targeted
|
* a field `port`: the port targeted
|
||||||
* a field `target`: the interface targeted by this service
|
* a field `target`: the interface targeted by this service
|
||||||
* a field `srv_entry`: the full entry to add in the DNS for this SRV record
|
* a field `srv_entry`: the full entry to add in the DNS for this
|
||||||
|
SRV record
|
||||||
"""
|
"""
|
||||||
srv = Srv.objects.all().select_related('extension').select_related('target__extension')
|
|
||||||
|
srv = (Srv.objects.all()
|
||||||
|
.select_related('extension')
|
||||||
|
.select_related('target__extension'))
|
||||||
seria = SrvSerializer(srv, many=True)
|
seria = SrvSerializer(srv, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
|
||||||
|
@ -311,8 +384,8 @@ def dns_srv(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def dns_zones(request):
|
def dns_zones(_request):
|
||||||
"""The list of the zones managed
|
"""The list of the zones managed
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
GET:
|
GET:
|
||||||
|
@ -320,21 +393,27 @@ def dns_zones(request):
|
||||||
* a list of dictionnaries (one for each zone) containing:
|
* a list of dictionnaries (one for each zone) containing:
|
||||||
* a field `name`: the extension for the zone
|
* a field `name`: the extension for the zone
|
||||||
* a field `origin`: the server IPv4 for the orgin of the zone
|
* a field `origin`: the server IPv4 for the orgin of the zone
|
||||||
* a field `origin_v6`: `null` if ipv6 is deactivated else the server IPv6 for the origin of the zone
|
* a field `origin_v6`: `null` if ipv6 is deactivated else the
|
||||||
|
server IPv6 for the origin of the zone
|
||||||
* a field `soa` containing:
|
* a field `soa` containing:
|
||||||
* a field `mail` containing the mail to contact in case of problem with the zone
|
* a field `mail` containing the mail to contact in case of
|
||||||
* a field `param` containing the full soa paramters to use in the DNS for this zone
|
problem with the zone
|
||||||
* a field `zone_entry`: the full entry to add in the DNS for the origin of the zone
|
* a field `param` containing the full soa paramters to use
|
||||||
|
in the DNS for this zone
|
||||||
|
* a field `zone_entry`: the full entry to add in the DNS for the
|
||||||
|
origin of the zone
|
||||||
"""
|
"""
|
||||||
|
|
||||||
zones = Extension.objects.all().select_related('origin')
|
zones = Extension.objects.all().select_related('origin')
|
||||||
seria = ExtensionSerializer(zones, many=True)
|
seria = ExtensionSerializer(zones, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def firewall_ouverture_ports(request):
|
def firewall_ouverture_ports(_request):
|
||||||
"""The list of the ports authorized to be openned by the firewall
|
"""The list of the ports authorized to be openned by the firewall
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -359,37 +438,73 @@ def firewall_ouverture_ports(request):
|
||||||
* a field `udp_out` containing:
|
* a field `udp_out` containing:
|
||||||
* a list of port number where ipv6 udp out should be ok
|
* a list of port number where ipv6 udp out should be ok
|
||||||
"""
|
"""
|
||||||
r = {'ipv4':{}, 'ipv6':{}}
|
|
||||||
for o in OuverturePortList.objects.all().prefetch_related('ouvertureport_set').prefetch_related('interface_set', 'interface_set__ipv4'):
|
r = {'ipv4': {}, 'ipv6': {}}
|
||||||
|
for o in (OuverturePortList.objects.all()
|
||||||
|
.prefetch_related('ouvertureport_set')
|
||||||
|
.prefetch_related('interface_set', 'interface_set__ipv4')):
|
||||||
pl = {
|
pl = {
|
||||||
"tcp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.IN))),
|
"tcp_in": set(map(
|
||||||
"tcp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.OUT))),
|
str,
|
||||||
"udp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.IN))),
|
o.ouvertureport_set.filter(
|
||||||
"udp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.OUT))),
|
protocole=OuverturePort.TCP,
|
||||||
|
io=OuverturePort.IN
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
"tcp_out": set(map(
|
||||||
|
str,
|
||||||
|
o.ouvertureport_set.filter(
|
||||||
|
protocole=OuverturePort.TCP,
|
||||||
|
io=OuverturePort.OUT
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
"udp_in": set(map(
|
||||||
|
str,
|
||||||
|
o.ouvertureport_set.filter(
|
||||||
|
protocole=OuverturePort.UDP,
|
||||||
|
io=OuverturePort.IN
|
||||||
|
)
|
||||||
|
)),
|
||||||
|
"udp_out": set(map(
|
||||||
|
str,
|
||||||
|
o.ouvertureport_set.filter(
|
||||||
|
protocole=OuverturePort.UDP,
|
||||||
|
io=OuverturePort.OUT
|
||||||
|
)
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
for i in filter_active_interfaces(o.interface_set):
|
for i in filter_active_interfaces(o.interface_set):
|
||||||
if i.may_have_port_open():
|
if i.may_have_port_open():
|
||||||
d = r['ipv4'].get(i.ipv4.ipv4, {})
|
d = r['ipv4'].get(i.ipv4.ipv4, {})
|
||||||
d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"])
|
d["tcp_in"] = (d.get("tcp_in", set())
|
||||||
d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"])
|
.union(pl["tcp_in"]))
|
||||||
d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"])
|
d["tcp_out"] = (d.get("tcp_out", set())
|
||||||
d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"])
|
.union(pl["tcp_out"]))
|
||||||
|
d["udp_in"] = (d.get("udp_in", set())
|
||||||
|
.union(pl["udp_in"]))
|
||||||
|
d["udp_out"] = (d.get("udp_out", set())
|
||||||
|
.union(pl["udp_out"]))
|
||||||
r['ipv4'][i.ipv4.ipv4] = d
|
r['ipv4'][i.ipv4.ipv4] = d
|
||||||
if i.ipv6():
|
if i.ipv6():
|
||||||
for ipv6 in i.ipv6():
|
for ipv6 in i.ipv6():
|
||||||
d = r['ipv6'].get(ipv6.ipv6, {})
|
d = r['ipv6'].get(ipv6.ipv6, {})
|
||||||
d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"])
|
d["tcp_in"] = (d.get("tcp_in", set())
|
||||||
d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"])
|
.union(pl["tcp_in"]))
|
||||||
d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"])
|
d["tcp_out"] = (d.get("tcp_out", set())
|
||||||
d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"])
|
.union(pl["tcp_out"]))
|
||||||
|
d["udp_in"] = (d.get("udp_in", set())
|
||||||
|
.union(pl["udp_in"]))
|
||||||
|
d["udp_out"] = (d.get("udp_out", set())
|
||||||
|
.union(pl["udp_out"]))
|
||||||
r['ipv6'][ipv6.ipv6] = d
|
r['ipv6'][ipv6.ipv6] = d
|
||||||
return JSONSuccess(r)
|
return JSONSuccess(r)
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def dhcp_mac_ip(request):
|
def dhcp_mac_ip(_request):
|
||||||
"""The list of all active interfaces with all the associated infos
|
"""The list of all active interfaces with all the associated infos
|
||||||
(MAC, IP, IpType, DNS name and associated zone extension)
|
(MAC, IP, IpType, DNS name and associated zone extension)
|
||||||
|
|
||||||
|
@ -402,8 +517,10 @@ def dhcp_mac_ip(request):
|
||||||
* a field `ip_type`: the name of the IpType of this interface
|
* a field `ip_type`: the name of the IpType of this interface
|
||||||
* a field `mac_address`: the MAC of this interface
|
* a field `mac_address`: the MAC of this interface
|
||||||
* a field `domain`: the DNS name for this interface
|
* a field `domain`: the DNS name for this interface
|
||||||
* a field `extension`: the extension for the DNS zone of this interface
|
* a field `extension`: the extension for the DNS zone of this
|
||||||
|
interface
|
||||||
"""
|
"""
|
||||||
|
|
||||||
interfaces = all_active_assigned_interfaces()
|
interfaces = all_active_assigned_interfaces()
|
||||||
seria = InterfaceSerializer(interfaces, many=True)
|
seria = InterfaceSerializer(interfaces, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
@ -413,7 +530,7 @@ def dhcp_mac_ip(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def mailing_standard(request):
|
def mailing_standard(_request):
|
||||||
"""All the available standard mailings.
|
"""All the available standard mailings.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -422,15 +539,17 @@ def mailing_standard(request):
|
||||||
* a list of dictionnaries (one for each mailing) containing:
|
* a list of dictionnaries (one for each mailing) containing:
|
||||||
* a field `name`: the name of a mailing
|
* a field `name`: the name of a mailing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return JSONSuccess([
|
return JSONSuccess([
|
||||||
{'name': 'adherents'}
|
{'name': 'adherents'}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def mailing_standard_ml_members(request):
|
def mailing_standard_ml_members(_request, ml_name):
|
||||||
"""All the members of a specific standard mailing
|
"""All the members of a specific standard mailing
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -442,6 +561,7 @@ def mailing_standard_ml_members(request):
|
||||||
* a field `surname`: the surname of the member
|
* a field `surname`: the surname of the member
|
||||||
* a field `pseudo`: the pseudo of the member
|
* a field `pseudo`: the pseudo of the member
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# All with active connextion
|
# All with active connextion
|
||||||
if ml_name == 'adherents':
|
if ml_name == 'adherents':
|
||||||
members = all_has_access().values('email').distinct()
|
members = all_has_access().values('email').distinct()
|
||||||
|
@ -456,7 +576,7 @@ def mailing_standard_ml_members(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def mailing_club(request):
|
def mailing_club(_request):
|
||||||
"""All the available club mailings.
|
"""All the available club mailings.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -465,15 +585,17 @@ def mailing_club(request):
|
||||||
* a list of dictionnaries (one for each mailing) containing:
|
* a list of dictionnaries (one for each mailing) containing:
|
||||||
* a field `name` indicating the name of a mailing
|
* a field `name` indicating the name of a mailing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
clubs = Club.objects.filter(mailing=True).values('pseudo')
|
clubs = Club.objects.filter(mailing=True).values('pseudo')
|
||||||
seria = MailingSerializer(clubs, many=True)
|
seria = MailingSerializer(clubs, many=True)
|
||||||
return JSONSuccess(seria.data)
|
return JSONSuccess(seria.data)
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
@accept_method(['GET'])
|
@accept_method(['GET'])
|
||||||
def mailing_club_ml_members(request):
|
def mailing_club_ml_members(_request, ml_name):
|
||||||
"""All the members of a specific club mailing
|
"""All the members of a specific club mailing
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -485,6 +607,7 @@ def mailing_club_ml_members(request):
|
||||||
* a field `surname`: the surname of the member
|
* a field `surname`: the surname of the member
|
||||||
* a field `pseudo`: the pseudo of the member
|
* a field `pseudo`: the pseudo of the member
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
club = Club.objects.get(mailing=True, pseudo=ml_name)
|
club = Club.objects.get(mailing=True, pseudo=ml_name)
|
||||||
except Club.DoesNotExist:
|
except Club.DoesNotExist:
|
||||||
|
|
|
@ -20,5 +20,8 @@
|
||||||
# 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.
|
||||||
|
"""cotisations
|
||||||
|
The app in charge of all the members's cotisations
|
||||||
|
"""
|
||||||
|
|
||||||
from .acl import *
|
from .acl import *
|
||||||
|
|
|
@ -27,6 +27,7 @@ Here are defined some functions to check acl on the application.
|
||||||
"""
|
"""
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
|
||||||
def can_view(user):
|
def can_view(user):
|
||||||
"""Check if an user can view the application.
|
"""Check if an user can view the application.
|
||||||
|
|
||||||
|
@ -38,4 +39,7 @@ def can_view(user):
|
||||||
viewing is granted and msg is a message (can be None).
|
viewing is granted and msg is a message (can be None).
|
||||||
"""
|
"""
|
||||||
can = user.has_module_perms('cotisations')
|
can = user.has_module_perms('cotisations')
|
||||||
return can, None if can else _("You don't have the rights to see this application.")
|
if can:
|
||||||
|
return can, None
|
||||||
|
else:
|
||||||
|
return can, _("You don't have the rights to see this application.")
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
# 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.
|
||||||
|
"""cotisations.admin
|
||||||
|
The objects, fields and datastructures visible in the Django admin view
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
# 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.
|
||||||
"""
|
"""
|
||||||
Forms for the 'cotisation' app of re2o. It highly depends on
|
Forms for the 'cotisation' app of re2o. It highly depends on
|
||||||
:cotisations:models and is mainly used by :cotisations:views.
|
:cotisations:models and is mainly used by :cotisations:views.
|
||||||
|
|
||||||
The following forms are mainly used to create, edit or delete
|
The following forms are mainly used to create, edit or delete
|
||||||
|
@ -38,16 +38,15 @@ from __future__ import unicode_literals
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.forms import ModelForm, Form
|
from django.forms import ModelForm, Form
|
||||||
from django.core.validators import MinValueValidator,MaxValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import ugettext_lazy as _l
|
from django.utils.translation import ugettext_lazy as _l
|
||||||
|
|
||||||
from .models import Article, Paiement, Facture, Banque
|
|
||||||
from preferences.models import OptionalUser
|
from preferences.models import OptionalUser
|
||||||
from users.models import User
|
|
||||||
|
|
||||||
from re2o.field_permissions import FieldPermissionFormMixin
|
from re2o.field_permissions import FieldPermissionFormMixin
|
||||||
from re2o.mixins import FormRevMixin
|
from re2o.mixins import FormRevMixin
|
||||||
|
from .models import Article, Paiement, Facture, Banque
|
||||||
|
|
||||||
|
|
||||||
class NewFactureForm(FormRevMixin, ModelForm):
|
class NewFactureForm(FormRevMixin, ModelForm):
|
||||||
"""
|
"""
|
||||||
|
@ -109,12 +108,16 @@ class CreditSoldeForm(NewFactureForm):
|
||||||
montant = forms.DecimalField(max_digits=5, decimal_places=2, required=True)
|
montant = forms.DecimalField(max_digits=5, decimal_places=2, required=True)
|
||||||
|
|
||||||
|
|
||||||
class SelectUserArticleForm(FormRevMixin, Form):
|
class SelectUserArticleForm(
|
||||||
|
FormRevMixin, Form):
|
||||||
"""
|
"""
|
||||||
Form used to select an article during the creation of an invoice for a member.
|
Form used to select an article during the creation of an invoice for a
|
||||||
|
member.
|
||||||
"""
|
"""
|
||||||
article = forms.ModelChoiceField(
|
article = forms.ModelChoiceField(
|
||||||
queryset=Article.objects.filter(Q(type_user='All') | Q(type_user='Adherent')),
|
queryset=Article.objects.filter(
|
||||||
|
Q(type_user='All') | Q(type_user='Adherent')
|
||||||
|
),
|
||||||
label=_l("Article"),
|
label=_l("Article"),
|
||||||
required=True
|
required=True
|
||||||
)
|
)
|
||||||
|
@ -127,10 +130,13 @@ class SelectUserArticleForm(FormRevMixin, Form):
|
||||||
|
|
||||||
class SelectClubArticleForm(Form):
|
class SelectClubArticleForm(Form):
|
||||||
"""
|
"""
|
||||||
Form used to select an article during the creation of an invoice for a club.
|
Form used to select an article during the creation of an invoice for a
|
||||||
|
club.
|
||||||
"""
|
"""
|
||||||
article = forms.ModelChoiceField(
|
article = forms.ModelChoiceField(
|
||||||
queryset=Article.objects.filter(Q(type_user='All') | Q(type_user='Club')),
|
queryset=Article.objects.filter(
|
||||||
|
Q(type_user='All') | Q(type_user='Club')
|
||||||
|
),
|
||||||
label=_l("Article"),
|
label=_l("Article"),
|
||||||
required=True
|
required=True
|
||||||
)
|
)
|
||||||
|
@ -140,6 +146,7 @@ class SelectClubArticleForm(Form):
|
||||||
required=True
|
required=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# TODO : change Facture to Invoice
|
# TODO : change Facture to Invoice
|
||||||
class NewFactureFormPdf(Form):
|
class NewFactureFormPdf(Form):
|
||||||
"""
|
"""
|
||||||
|
@ -147,9 +154,18 @@ class NewFactureFormPdf(Form):
|
||||||
"""
|
"""
|
||||||
paid = forms.BooleanField(label=_l("Paid"), required=False)
|
paid = forms.BooleanField(label=_l("Paid"), required=False)
|
||||||
# TODO : change dest field to recipient
|
# TODO : change dest field to recipient
|
||||||
dest = forms.CharField(required=True, max_length=255, label=_l("Recipient"))
|
dest = forms.CharField(
|
||||||
|
required=True,
|
||||||
|
max_length=255,
|
||||||
|
label=_l("Recipient")
|
||||||
|
)
|
||||||
# TODO : change chambre field to address
|
# TODO : change chambre field to address
|
||||||
chambre = forms.CharField(required=False, max_length=10, label=_l("Address"))
|
chambre = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
max_length=10,
|
||||||
|
label=_l("Address")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# TODO : change Facture to Invoice
|
# TODO : change Facture to Invoice
|
||||||
class EditFactureForm(FieldPermissionFormMixin, NewFactureForm):
|
class EditFactureForm(FieldPermissionFormMixin, NewFactureForm):
|
||||||
|
@ -295,6 +311,11 @@ class NewFactureSoldeForm(NewFactureForm):
|
||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
|
super(NewFactureSoldeForm, self).__init__(
|
||||||
|
*args,
|
||||||
|
prefix=prefix,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
self.fields['cheque'].required = False
|
self.fields['cheque'].required = False
|
||||||
self.fields['banque'].required = False
|
self.fields['banque'].required = False
|
||||||
self.fields['cheque'].label = _('Cheque number')
|
self.fields['cheque'].label = _('Cheque number')
|
||||||
|
@ -313,7 +334,6 @@ class NewFactureSoldeForm(NewFactureForm):
|
||||||
# TODO : change paiement to payment and baque to bank
|
# TODO : change paiement to payment and baque to bank
|
||||||
fields = ['paiement', 'banque']
|
fields = ['paiement', 'banque']
|
||||||
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
cleaned_data = super(NewFactureSoldeForm, self).clean()
|
cleaned_data = super(NewFactureSoldeForm, self).clean()
|
||||||
# TODO : change paiement to payment
|
# TODO : change paiement to payment
|
||||||
|
@ -342,7 +362,7 @@ class RechargeForm(FormRevMixin, Form):
|
||||||
value = forms.FloatField(
|
value = forms.FloatField(
|
||||||
label=_l("Amount"),
|
label=_l("Amount"),
|
||||||
min_value=0.01,
|
min_value=0.01,
|
||||||
validators = []
|
validators=[]
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -350,6 +370,10 @@ class RechargeForm(FormRevMixin, Form):
|
||||||
super(RechargeForm, self).__init__(*args, **kwargs)
|
super(RechargeForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def clean_value(self):
|
def clean_value(self):
|
||||||
|
"""
|
||||||
|
Returns a cleaned vlaue from the received form by validating
|
||||||
|
the value is well inside the possible limits
|
||||||
|
"""
|
||||||
value = self.cleaned_data['value']
|
value = self.cleaned_data['value']
|
||||||
if value < OptionalUser.get_cached_value('min_online_payment'):
|
if value < OptionalUser.get_cached_value('min_online_payment'):
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
|
@ -360,7 +384,8 @@ class RechargeForm(FormRevMixin, Form):
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if value + self.user.solde > OptionalUser.get_cached_value('max_solde'):
|
if value + self.user.solde > \
|
||||||
|
OptionalUser.get_cached_value('max_solde'):
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
_("Requested amount is too high. Your balance can't exceed \
|
_("Requested amount is too high. Your balance can't exceed \
|
||||||
%(max_online_balance)s €.") % {
|
%(max_online_balance)s €.") % {
|
||||||
|
|
|
@ -34,17 +34,16 @@ from __future__ import unicode_literals
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q
|
from django.db.models import Q, Max
|
||||||
from django.db.models.signals import post_save, post_delete
|
from django.db.models.signals import post_save, post_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
from django.db.models import Max
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from machines.models import regen
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import ugettext_lazy as _l
|
from django.utils.translation import ugettext_lazy as _l
|
||||||
|
|
||||||
|
from machines.models import regen
|
||||||
from re2o.field_permissions import FieldPermissionModelMixin
|
from re2o.field_permissions import FieldPermissionModelMixin
|
||||||
from re2o.mixins import AclMixin, RevMixin
|
from re2o.mixins import AclMixin, RevMixin
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
The model for an invoice. It reprensents the fact that a user paid for
|
The model for an invoice. It reprensents the fact that a user paid for
|
||||||
something (it can be multiple article paid at once).
|
something (it can be multiple article paid at once).
|
||||||
|
|
||||||
An invoice is linked to :
|
An invoice is linked to :
|
||||||
* one or more purchases (one for each article sold that time)
|
* one or more purchases (one for each article sold that time)
|
||||||
* a user (the one who bought those articles)
|
* a user (the one who bought those articles)
|
||||||
|
@ -105,11 +104,16 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
abstract = False
|
abstract = False
|
||||||
permissions = (
|
permissions = (
|
||||||
# TODO : change facture to invoice
|
# TODO : change facture to invoice
|
||||||
('change_facture_control', _l("Can change the \"controlled\" state")),
|
('change_facture_control',
|
||||||
# TODO : seems more likely to be call create_facture_pdf or create_invoice_pdf
|
_l("Can change the \"controlled\" state")),
|
||||||
('change_facture_pdf', _l("Can create a custom PDF invoice")),
|
# TODO : seems more likely to be call create_facture_pdf
|
||||||
('view_facture', _l("Can see an invoice's details")),
|
# or create_invoice_pdf
|
||||||
('change_all_facture', _l("Can edit all the previous invoices")),
|
('change_facture_pdf',
|
||||||
|
_l("Can create a custom PDF invoice")),
|
||||||
|
('view_facture',
|
||||||
|
_l("Can see an invoice's details")),
|
||||||
|
('change_all_facture',
|
||||||
|
_l("Can edit all the previous invoices")),
|
||||||
)
|
)
|
||||||
verbose_name = _l("Invoice")
|
verbose_name = _l("Invoice")
|
||||||
verbose_name_plural = _l("Invoices")
|
verbose_name_plural = _l("Invoices")
|
||||||
|
@ -159,11 +163,14 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
def can_edit(self, user_request, *args, **kwargs):
|
def can_edit(self, user_request, *args, **kwargs):
|
||||||
if not user_request.has_perm('cotisations.change_facture'):
|
if not user_request.has_perm('cotisations.change_facture'):
|
||||||
return False, _("You don't have the right to edit an invoice.")
|
return False, _("You don't have the right to edit an invoice.")
|
||||||
elif not user_request.has_perm('cotisations.change_all_facture') and not self.user.can_edit(user_request, *args, **kwargs)[0]:
|
elif not user_request.has_perm('cotisations.change_all_facture') and \
|
||||||
return False, _("You don't have the right to edit this user's invoices.")
|
not self.user.can_edit(user_request, *args, **kwargs)[0]:
|
||||||
elif not user_request.has_perm('cotisations.change_all_facture') and\
|
return False, _("You don't have the right to edit this user's "
|
||||||
(self.control or not self.valid):
|
"invoices.")
|
||||||
return False, _("You don't have the right to edit an invoice already controlled or invalidated.")
|
elif not user_request.has_perm('cotisations.change_all_facture') and \
|
||||||
|
(self.control or not self.valid):
|
||||||
|
return False, _("You don't have the right to edit an invoice "
|
||||||
|
"already controlled or invalidated.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
@ -171,33 +178,45 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
if not user_request.has_perm('cotisations.delete_facture'):
|
if not user_request.has_perm('cotisations.delete_facture'):
|
||||||
return False, _("You don't have the right to delete an invoice.")
|
return False, _("You don't have the right to delete an invoice.")
|
||||||
if not self.user.can_edit(user_request, *args, **kwargs)[0]:
|
if not self.user.can_edit(user_request, *args, **kwargs)[0]:
|
||||||
return False, _("You don't have the right to delete this user's invoices.")
|
return False, _("You don't have the right to delete this user's "
|
||||||
|
"invoices.")
|
||||||
if self.control or not self.valid:
|
if self.control or not self.valid:
|
||||||
return False, _("You don't have the right to delete an invoice already controlled or invalidated.")
|
return False, _("You don't have the right to delete an invoice "
|
||||||
|
"already controlled or invalidated.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def can_view(self, user_request, *args, **kwargs):
|
def can_view(self, user_request, *_args, **_kwargs):
|
||||||
if not user_request.has_perm('cotisations.view_facture') and\
|
if not user_request.has_perm('cotisations.view_facture') and \
|
||||||
self.user != user_request:
|
self.user != user_request:
|
||||||
return False, _("You don't have the right to see someone else's invoices history.")
|
return False, _("You don't have the right to see someone else's "
|
||||||
|
"invoices history.")
|
||||||
elif not self.valid:
|
elif not self.valid:
|
||||||
return False, _("The invoice has been invalidated.")
|
return False, _("The invoice has been invalidated.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_change_control(user_request, *args, **kwargs):
|
def can_change_control(user_request, *_args, **_kwargs):
|
||||||
return user_request.has_perm('cotisations.change_facture_control'), _("You don't have the right to edit the \"controlled\" state.")
|
""" Returns True if the user can change the 'controlled' status of
|
||||||
|
this invoice """
|
||||||
|
return (
|
||||||
|
user_request.has_perm('cotisations.change_facture_control'),
|
||||||
|
_("You don't have the right to edit the \"controlled\" state.")
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_change_pdf(user_request, *args, **kwargs):
|
def can_change_pdf(user_request, *_args, **_kwargs):
|
||||||
return user_request.has_perm('cotisations.change_facture_pdf'), _("You don't have the right to edit an invoice.")
|
""" Returns True if the user can change this invoice """
|
||||||
|
return (
|
||||||
|
user_request.has_perm('cotisations.change_facture_pdf'),
|
||||||
|
_("You don't have the right to edit an invoice.")
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Facture, self).__init__(*args, **kwargs)
|
super(Facture, self).__init__(*args, **kwargs)
|
||||||
self.field_permissions = {
|
self.field_permissions = {
|
||||||
'control' : self.can_change_control,
|
'control': self.can_change_control,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -205,7 +224,7 @@ class Facture(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Facture)
|
@receiver(post_save, sender=Facture)
|
||||||
def facture_post_save(sender, **kwargs):
|
def facture_post_save(**kwargs):
|
||||||
"""
|
"""
|
||||||
Synchronise the LDAP user after an invoice has been saved.
|
Synchronise the LDAP user after an invoice has been saved.
|
||||||
"""
|
"""
|
||||||
|
@ -215,7 +234,7 @@ def facture_post_save(sender, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Facture)
|
@receiver(post_delete, sender=Facture)
|
||||||
def facture_post_delete(sender, **kwargs):
|
def facture_post_delete(**kwargs):
|
||||||
"""
|
"""
|
||||||
Synchronise the LDAP user after an invoice has been deleted.
|
Synchronise the LDAP user after an invoice has been deleted.
|
||||||
"""
|
"""
|
||||||
|
@ -228,7 +247,7 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
The model defining a purchase. It consist of one type of article being
|
The model defining a purchase. It consist of one type of article being
|
||||||
sold. In particular there may be multiple purchases in a single invoice.
|
sold. In particular there may be multiple purchases in a single invoice.
|
||||||
|
|
||||||
It's reprensentated by:
|
It's reprensentated by:
|
||||||
* an amount (the number of items sold)
|
* an amount (the number of items sold)
|
||||||
* an invoice (whose the purchase is part of)
|
* an invoice (whose the purchase is part of)
|
||||||
|
@ -289,7 +308,6 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
verbose_name = _l("Purchase")
|
verbose_name = _l("Purchase")
|
||||||
verbose_name_plural = _l("Purchases")
|
verbose_name_plural = _l("Purchases")
|
||||||
|
|
||||||
|
|
||||||
# TODO : change prix_total to total_price
|
# TODO : change prix_total to total_price
|
||||||
def prix_total(self):
|
def prix_total(self):
|
||||||
"""
|
"""
|
||||||
|
@ -323,11 +341,13 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
facture__in=Facture.objects.filter(
|
facture__in=Facture.objects.filter(
|
||||||
user=self.facture.user
|
user=self.facture.user
|
||||||
).exclude(valid=False))
|
).exclude(valid=False))
|
||||||
).filter(Q(type_cotisation='All') | Q(type_cotisation=self.type_cotisation)
|
).filter(
|
||||||
|
Q(type_cotisation='All') |
|
||||||
|
Q(type_cotisation=self.type_cotisation)
|
||||||
).filter(
|
).filter(
|
||||||
date_start__lt=date_start
|
date_start__lt=date_start
|
||||||
).aggregate(Max('date_end'))['date_end__max']
|
).aggregate(Max('date_end'))['date_end__max']
|
||||||
elif self.type_cotisation=="Adhesion":
|
elif self.type_cotisation == "Adhesion":
|
||||||
end_cotisation = self.facture.user.end_adhesion()
|
end_cotisation = self.facture.user.end_adhesion()
|
||||||
else:
|
else:
|
||||||
end_cotisation = self.facture.user.end_connexion()
|
end_cotisation = self.facture.user.end_connexion()
|
||||||
|
@ -357,11 +377,16 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
def can_edit(self, user_request, *args, **kwargs):
|
def can_edit(self, user_request, *args, **kwargs):
|
||||||
if not user_request.has_perm('cotisations.change_vente'):
|
if not user_request.has_perm('cotisations.change_vente'):
|
||||||
return False, _("You don't have the right to edit the purchases.")
|
return False, _("You don't have the right to edit the purchases.")
|
||||||
elif not user_request.has_perm('cotisations.change_all_facture') and not self.facture.user.can_edit(user_request, *args, **kwargs)[0]:
|
elif (not user_request.has_perm('cotisations.change_all_facture') and
|
||||||
return False, _("You don't have the right to edit this user's purchases.")
|
not self.facture.user.can_edit(
|
||||||
elif not user_request.has_perm('cotisations.change_all_vente') and\
|
user_request, *args, **kwargs
|
||||||
(self.facture.control or not self.facture.valid):
|
)[0]):
|
||||||
return False, _("You don't have the right to edit a purchase already controlled or invalidated.")
|
return False, _("You don't have the right to edit this user's "
|
||||||
|
"purchases.")
|
||||||
|
elif (not user_request.has_perm('cotisations.change_all_vente') and
|
||||||
|
(self.facture.control or not self.facture.valid)):
|
||||||
|
return False, _("You don't have the right to edit a purchase "
|
||||||
|
"already controlled or invalidated.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
@ -369,16 +394,19 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
if not user_request.has_perm('cotisations.delete_vente'):
|
if not user_request.has_perm('cotisations.delete_vente'):
|
||||||
return False, _("You don't have the right to delete a purchase.")
|
return False, _("You don't have the right to delete a purchase.")
|
||||||
if not self.facture.user.can_edit(user_request, *args, **kwargs)[0]:
|
if not self.facture.user.can_edit(user_request, *args, **kwargs)[0]:
|
||||||
return False, _("You don't have the right to delete this user's purchases.")
|
return False, _("You don't have the right to delete this user's "
|
||||||
|
"purchases.")
|
||||||
if self.facture.control or not self.facture.valid:
|
if self.facture.control or not self.facture.valid:
|
||||||
return False, _("You don't have the right to delete a purchase already controlled or invalidated.")
|
return False, _("You don't have the right to delete a purchase "
|
||||||
|
"already controlled or invalidated.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def can_view(self, user_request, *args, **kwargs):
|
def can_view(self, user_request, *_args, **_kwargs):
|
||||||
if not user_request.has_perm('cotisations.view_vente') and\
|
if (not user_request.has_perm('cotisations.view_vente') and
|
||||||
self.facture.user != user_request:
|
self.facture.user != user_request):
|
||||||
return False, _("You don't have the right to see someone else's purchase history.")
|
return False, _("You don't have the right to see someone "
|
||||||
|
"else's purchase history.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
@ -388,7 +416,7 @@ class Vente(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
# TODO : change vente to purchase
|
# TODO : change vente to purchase
|
||||||
@receiver(post_save, sender=Vente)
|
@receiver(post_save, sender=Vente)
|
||||||
def vente_post_save(sender, **kwargs):
|
def vente_post_save(**kwargs):
|
||||||
"""
|
"""
|
||||||
Creates a 'cotisation' related object if needed and synchronise the
|
Creates a 'cotisation' related object if needed and synchronise the
|
||||||
LDAP user when a purchase has been saved.
|
LDAP user when a purchase has been saved.
|
||||||
|
@ -406,7 +434,7 @@ def vente_post_save(sender, **kwargs):
|
||||||
|
|
||||||
# TODO : change vente to purchase
|
# TODO : change vente to purchase
|
||||||
@receiver(post_delete, sender=Vente)
|
@receiver(post_delete, sender=Vente)
|
||||||
def vente_post_delete(sender, **kwargs):
|
def vente_post_delete(**kwargs):
|
||||||
"""
|
"""
|
||||||
Synchronise the LDAP user after a purchase has been deleted.
|
Synchronise the LDAP user after a purchase has been deleted.
|
||||||
"""
|
"""
|
||||||
|
@ -418,12 +446,14 @@ def vente_post_delete(sender, **kwargs):
|
||||||
|
|
||||||
class Article(RevMixin, AclMixin, models.Model):
|
class Article(RevMixin, AclMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
The definition of an article model. It represents an type of object that can be sold to the user.
|
The definition of an article model. It represents a type of object
|
||||||
|
that can be sold to the user.
|
||||||
|
|
||||||
It's represented by:
|
It's represented by:
|
||||||
* a name
|
* a name
|
||||||
* a price
|
* a price
|
||||||
* a cotisation type (indicating if this article reprensents a cotisation or not)
|
* a cotisation type (indicating if this article reprensents a
|
||||||
|
cotisation or not)
|
||||||
* a duration (if it is a cotisation)
|
* a duration (if it is a cotisation)
|
||||||
* a type of user (indicating what kind of user can buy this article)
|
* a type of user (indicating what kind of user can buy this article)
|
||||||
"""
|
"""
|
||||||
|
@ -513,8 +543,8 @@ class Banque(RevMixin, AclMixin, models.Model):
|
||||||
permissions = (
|
permissions = (
|
||||||
('view_banque', _l("Can see a bank's details")),
|
('view_banque', _l("Can see a bank's details")),
|
||||||
)
|
)
|
||||||
verbose_name=_l("Bank")
|
verbose_name = _l("Bank")
|
||||||
verbose_name_plural=_l("Banks")
|
verbose_name_plural = _l("Banks")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -530,7 +560,7 @@ class Paiement(RevMixin, AclMixin, models.Model):
|
||||||
* a type (used for the type 'cheque' which implies the use of a bank
|
* a type (used for the type 'cheque' which implies the use of a bank
|
||||||
and an account number in related models)
|
and an account number in related models)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
PAYMENT_TYPES = (
|
PAYMENT_TYPES = (
|
||||||
(0, _l("Standard")),
|
(0, _l("Standard")),
|
||||||
(1, _l("Cheque")),
|
(1, _l("Cheque")),
|
||||||
|
@ -619,27 +649,32 @@ class Cotisation(RevMixin, AclMixin, models.Model):
|
||||||
('change_all_cotisation', _l("Can edit the previous cotisations")),
|
('change_all_cotisation', _l("Can edit the previous cotisations")),
|
||||||
)
|
)
|
||||||
|
|
||||||
def can_edit(self, user_request, *args, **kwargs):
|
def can_edit(self, user_request, *_args, **_kwargs):
|
||||||
if not user_request.has_perm('cotisations.change_cotisation'):
|
if not user_request.has_perm('cotisations.change_cotisation'):
|
||||||
return False, _("You don't have the right to edit a cotisation.")
|
return False, _("You don't have the right to edit a cotisation.")
|
||||||
elif not user_request.has_perm('cotisations.change_all_cotisation') and\
|
elif not user_request.has_perm('cotisations.change_all_cotisation') \
|
||||||
(self.vente.facture.control or not self.vente.facture.valid):
|
and (self.vente.facture.control or
|
||||||
return False, _("You don't have the right to edit a cotisation already controlled or invalidated.")
|
not self.vente.facture.valid):
|
||||||
|
return False, _("You don't have the right to edit a cotisation "
|
||||||
|
"already controlled or invalidated.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def can_delete(self, user_request, *args, **kwargs):
|
def can_delete(self, user_request, *_args, **_kwargs):
|
||||||
if not user_request.has_perm('cotisations.delete_cotisation'):
|
if not user_request.has_perm('cotisations.delete_cotisation'):
|
||||||
return False, _("You don't have the right to delete a cotisation.")
|
return False, _("You don't have the right to delete a "
|
||||||
|
"cotisation.")
|
||||||
if self.vente.facture.control or not self.vente.facture.valid:
|
if self.vente.facture.control or not self.vente.facture.valid:
|
||||||
return False, _("You don't have the right to delete a cotisation already controlled or invalidated.")
|
return False, _("You don't have the right to delete a cotisation "
|
||||||
|
"already controlled or invalidated.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def can_view(self, user_request, *args, **kwargs):
|
def can_view(self, user_request, *_args, **_kwargs):
|
||||||
if not user_request.has_perm('cotisations.view_cotisation') and\
|
if not user_request.has_perm('cotisations.view_cotisation') and\
|
||||||
self.vente.facture.user != user_request:
|
self.vente.facture.user != user_request:
|
||||||
return False, _("You don't have the right to see someone else's cotisation history.")
|
return False, _("You don't have the right to see someone else's "
|
||||||
|
"cotisation history.")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
@ -648,7 +683,7 @@ class Cotisation(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Cotisation)
|
@receiver(post_save, sender=Cotisation)
|
||||||
def cotisation_post_save(sender, **kwargs):
|
def cotisation_post_save(**_kwargs):
|
||||||
"""
|
"""
|
||||||
Mark some services as needing a regeneration after the edition of a
|
Mark some services as needing a regeneration after the edition of a
|
||||||
cotisation. Indeed the membership status may have changed.
|
cotisation. Indeed the membership status may have changed.
|
||||||
|
@ -660,11 +695,10 @@ def cotisation_post_save(sender, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Cotisation)
|
@receiver(post_delete, sender=Cotisation)
|
||||||
def cotisation_post_delete(sender, **kwargs):
|
def cotisation_post_delete(**_kwargs):
|
||||||
"""
|
"""
|
||||||
Mark some services as needing a regeneration after the deletion of a
|
Mark some services as needing a regeneration after the deletion of a
|
||||||
cotisation. Indeed the membership status may have changed.
|
cotisation. Indeed the membership status may have changed.
|
||||||
"""
|
"""
|
||||||
cotisation = kwargs['instance']
|
|
||||||
regen('mac_ip_list')
|
regen('mac_ip_list')
|
||||||
regen('mailing')
|
regen('mailing')
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
Here are defined some views dedicated to online payement.
|
Here are defined some views dedicated to online payement.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import redirect, get_object_or_404
|
from django.shortcuts import redirect, get_object_or_404
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
@ -11,12 +14,11 @@ from django.utils.datastructures import MultiValueDictKeyError
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.http import HttpResponse, HttpResponseBadRequest
|
from django.http import HttpResponse, HttpResponseBadRequest
|
||||||
|
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
from preferences.models import AssoOption
|
from preferences.models import AssoOption
|
||||||
from .models import Facture
|
from .models import Facture
|
||||||
from .payment_utils.comnpay import Payment as ComnpayPayment
|
from .payment_utils.comnpay import Payment as ComnpayPayment
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required
|
@login_required
|
||||||
def accept_payment(request, factureid):
|
def accept_payment(request, factureid):
|
||||||
|
@ -30,7 +32,10 @@ def accept_payment(request, factureid):
|
||||||
'amount': facture.prix()
|
'amount': facture.prix()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return redirect(reverse('users:profil', kwargs={'userid':request.user.id}))
|
return redirect(reverse(
|
||||||
|
'users:profil',
|
||||||
|
kwargs={'userid': request.user.id}
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
|
@ -43,7 +48,11 @@ def refuse_payment(request):
|
||||||
request,
|
request,
|
||||||
_("The payment has been refused.")
|
_("The payment has been refused.")
|
||||||
)
|
)
|
||||||
return redirect(reverse('users:profil', kwargs={'userid':request.user.id}))
|
return redirect(reverse(
|
||||||
|
'users:profil',
|
||||||
|
kwargs={'userid': request.user.id}
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def ipn(request):
|
def ipn(request):
|
||||||
|
@ -105,7 +114,7 @@ def comnpay(facture, request):
|
||||||
str(AssoOption.get_cached_value('payment_pass')),
|
str(AssoOption.get_cached_value('payment_pass')),
|
||||||
'https://' + host + reverse(
|
'https://' + host + reverse(
|
||||||
'cotisations:accept_payment',
|
'cotisations:accept_payment',
|
||||||
kwargs={'factureid':facture.id}
|
kwargs={'factureid': facture.id}
|
||||||
),
|
),
|
||||||
'https://' + host + reverse('cotisations:refuse_payment'),
|
'https://' + host + reverse('cotisations:refuse_payment'),
|
||||||
'https://' + host + reverse('cotisations:ipn'),
|
'https://' + host + reverse('cotisations:ipn'),
|
||||||
|
@ -113,20 +122,20 @@ def comnpay(facture, request):
|
||||||
"D"
|
"D"
|
||||||
)
|
)
|
||||||
r = {
|
r = {
|
||||||
'action' : 'https://secure.homologation.comnpay.com',
|
'action': 'https://secure.homologation.comnpay.com',
|
||||||
'method' : 'POST',
|
'method': 'POST',
|
||||||
'content' : p.buildSecretHTML(
|
'content': p.buildSecretHTML(
|
||||||
"Rechargement du solde",
|
"Rechargement du solde",
|
||||||
facture.prix(),
|
facture.prix(),
|
||||||
idTransaction=str(facture.id)
|
idTransaction=str(facture.id)
|
||||||
),
|
),
|
||||||
'amount' : facture.prix,
|
'amount': facture.prix,
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
# The payment systems supported by re2o
|
# The payment systems supported by re2o
|
||||||
PAYMENT_SYSTEM = {
|
PAYMENT_SYSTEM = {
|
||||||
'COMNPAY' : comnpay,
|
'COMNPAY': comnpay,
|
||||||
'NONE' : None
|
'NONE': None
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
|
"""cotisations.payment_utils.comnpay
|
||||||
|
The module in charge of handling the negociation with Comnpay
|
||||||
|
for online payment
|
||||||
|
"""
|
||||||
|
|
||||||
import time
|
import time
|
||||||
from random import randrange
|
from random import randrange
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from itertools import chain
|
|
||||||
|
|
||||||
class Payment():
|
class Payment():
|
||||||
|
""" The class representing a transaction with all the functions
|
||||||
|
used during the negociation
|
||||||
|
"""
|
||||||
|
|
||||||
vad_number = ""
|
def __init__(self, vad_number="", secret_key="", urlRetourOK="",
|
||||||
secret_key = ""
|
urlRetourNOK="", urlIPN="", source="", typeTr="D"):
|
||||||
urlRetourOK = ""
|
|
||||||
urlRetourNOK = ""
|
|
||||||
urlIPN = ""
|
|
||||||
source = ""
|
|
||||||
typeTr = "D"
|
|
||||||
|
|
||||||
def __init__(self, vad_number = "", secret_key = "", urlRetourOK = "", urlRetourNOK = "", urlIPN = "", source="", typeTr="D"):
|
|
||||||
self.vad_number = vad_number
|
self.vad_number = vad_number
|
||||||
self.secret_key = secret_key
|
self.secret_key = secret_key
|
||||||
self.urlRetourOK = urlRetourOK
|
self.urlRetourOK = urlRetourOK
|
||||||
|
@ -23,46 +24,63 @@ class Payment():
|
||||||
self.urlIPN = urlIPN
|
self.urlIPN = urlIPN
|
||||||
self.source = source
|
self.source = source
|
||||||
self.typeTr = typeTr
|
self.typeTr = typeTr
|
||||||
|
self.idTransaction = ""
|
||||||
|
|
||||||
def buildSecretHTML(self, produit="Produit", montant="0.00", idTransaction=""):
|
def buildSecretHTML(self, produit="Produit", montant="0.00",
|
||||||
|
idTransaction=""):
|
||||||
|
""" Build an HTML hidden form with the different parameters for the
|
||||||
|
transaction
|
||||||
|
"""
|
||||||
if idTransaction == "":
|
if idTransaction == "":
|
||||||
self.idTransaction = str(time.time())+self.vad_number+str(randrange(999))
|
self.idTransaction = str(time.time())
|
||||||
|
self.idTransaction += self.vad_number
|
||||||
|
self.idTransaction += str(randrange(999))
|
||||||
else:
|
else:
|
||||||
self.idTransaction = idTransaction
|
self.idTransaction = idTransaction
|
||||||
|
|
||||||
array_tpe = OrderedDict(
|
array_tpe = OrderedDict(
|
||||||
montant= str(montant),
|
montant=str(montant),
|
||||||
idTPE= self.vad_number,
|
idTPE=self.vad_number,
|
||||||
idTransaction= self.idTransaction,
|
idTransaction=self.idTransaction,
|
||||||
devise= "EUR",
|
devise="EUR",
|
||||||
lang= 'fr',
|
lang='fr',
|
||||||
nom_produit= produit,
|
nom_produit=produit,
|
||||||
source= self.source,
|
source=self.source,
|
||||||
urlRetourOK= self.urlRetourOK,
|
urlRetourOK=self.urlRetourOK,
|
||||||
urlRetourNOK= self.urlRetourNOK,
|
urlRetourNOK=self.urlRetourNOK,
|
||||||
typeTr= str(self.typeTr)
|
typeTr=str(self.typeTr)
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.urlIPN!="":
|
if self.urlIPN != "":
|
||||||
array_tpe['urlIPN'] = self.urlIPN
|
array_tpe['urlIPN'] = self.urlIPN
|
||||||
|
|
||||||
array_tpe['key'] = self.secret_key;
|
array_tpe['key'] = self.secret_key
|
||||||
strWithKey = base64.b64encode(bytes('|'.join(array_tpe.values()), 'utf-8'))
|
strWithKey = base64.b64encode(bytes(
|
||||||
|
'|'.join(array_tpe.values()),
|
||||||
|
'utf-8'
|
||||||
|
))
|
||||||
del array_tpe["key"]
|
del array_tpe["key"]
|
||||||
array_tpe['sec'] = hashlib.sha512(strWithKey).hexdigest()
|
array_tpe['sec'] = hashlib.sha512(strWithKey).hexdigest()
|
||||||
|
|
||||||
ret = ""
|
ret = ""
|
||||||
for key in array_tpe:
|
for key in array_tpe:
|
||||||
ret += '<input type="hidden" name="'+key+'" value="'+array_tpe[key]+'"/>'
|
ret += '<input type="hidden" name="{k}" value="{v}"/>'.format(
|
||||||
|
k=key,
|
||||||
|
v=array_tpe[key]
|
||||||
|
)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def validSec(self, values, secret_key):
|
@staticmethod
|
||||||
|
def validSec(values, secret_key):
|
||||||
|
""" Check if the secret value is correct """
|
||||||
if "sec" in values:
|
if "sec" in values:
|
||||||
sec = values['sec']
|
sec = values['sec']
|
||||||
del values["sec"]
|
del values["sec"]
|
||||||
strWithKey = hashlib.sha512(base64.b64encode(bytes('|'.join(values.values()) +"|"+secret_key, 'utf-8'))).hexdigest()
|
strWithKey = hashlib.sha512(base64.b64encode(bytes(
|
||||||
|
'|'.join(values.values()) + "|" + secret_key,
|
||||||
|
'utf-8'
|
||||||
|
))).hexdigest()
|
||||||
return strWithKey.upper() == sec.upper()
|
return strWithKey.upper() == sec.upper()
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
<form class="form" method="post">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
{% if articlesformset %}
|
||||||
<h3>{% trans "Invoice's articles" %}</h3>
|
<h3>{% trans "Invoice's articles" %}</h3>
|
||||||
<div id="form_set" class="form-group">
|
<div id="form_set" class="form-group">
|
||||||
{{ articlesformset.management_form }}
|
{{ articlesformset.management_form }}
|
||||||
|
@ -54,11 +55,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
Total price : <span id="total_price">0,00</span> €
|
Total price : <span id="total_price">0,00</span> €
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
</p>
|
</p>
|
||||||
|
{% endif %}
|
||||||
{% bootstrap_form factureform %}
|
{% bootstrap_form factureform %}
|
||||||
{% bootstrap_button action_name button_type='submit' icon='star' %}
|
{% bootstrap_button action_name button_type='submit' icon='star' %}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
{% if articlesformset %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var prices = {};
|
var prices = {};
|
||||||
{% for article in articles %}
|
{% for article in articles %}
|
||||||
|
@ -133,6 +135,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
update_price();
|
update_price();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
# 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.
|
||||||
|
"""cotisations.tests
|
||||||
|
The tests for the Cotisations module.
|
||||||
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
# from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -24,40 +24,44 @@ Module in charge of rendering some LaTex templates.
|
||||||
Used to generated PDF invoice.
|
Used to generated PDF invoice.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import tempfile
|
||||||
|
from subprocess import Popen, PIPE
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.template import Context
|
from django.template import Context
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
||||||
import tempfile
|
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
TEMP_PREFIX = getattr(settings, 'TEX_TEMP_PREFIX', 'render_tex-')
|
TEMP_PREFIX = getattr(settings, 'TEX_TEMP_PREFIX', 'render_tex-')
|
||||||
CACHE_PREFIX = getattr(settings, 'TEX_CACHE_PREFIX', 'render-tex')
|
CACHE_PREFIX = getattr(settings, 'TEX_CACHE_PREFIX', 'render-tex')
|
||||||
CACHE_TIMEOUT = getattr(settings, 'TEX_CACHE_TIMEOUT', 86400) # 1 day
|
CACHE_TIMEOUT = getattr(settings, 'TEX_CACHE_TIMEOUT', 86400) # 1 day
|
||||||
|
|
||||||
|
|
||||||
def render_invoice(request, ctx={}):
|
def render_invoice(_request, ctx={}):
|
||||||
"""
|
"""
|
||||||
Render an invoice using some available information such as the current
|
Render an invoice using some available information such as the current
|
||||||
date, the user, the articles, the prices, ...
|
date, the user, the articles, the prices, ...
|
||||||
"""
|
"""
|
||||||
filename = '_'.join([
|
filename = '_'.join([
|
||||||
'invoice',
|
'invoice',
|
||||||
slugify(ctx['asso_name']),
|
slugify(ctx.get('asso_name', "")),
|
||||||
slugify(ctx['recipient_name']),
|
slugify(ctx.get('recipient_name', "")),
|
||||||
str(ctx['DATE'].year),
|
str(ctx.get('DATE', datetime.now()).year),
|
||||||
str(ctx['DATE'].month),
|
str(ctx.get('DATE', datetime.now()).month),
|
||||||
str(ctx['DATE'].day),
|
str(ctx.get('DATE', datetime.now()).day),
|
||||||
])
|
])
|
||||||
r = render_tex(request, 'cotisations/factures.tex', ctx)
|
r = render_tex(_request, 'cotisations/factures.tex', ctx)
|
||||||
r['Content-Disposition'] = ''.join(['attachment; filename="',filename,'.pdf"'])
|
r['Content-Disposition'] = 'attachment; filename="{name}.pdf"'.format(
|
||||||
|
name=filename
|
||||||
|
)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def render_tex(request, template, ctx={}):
|
|
||||||
|
def render_tex(_request, template, ctx={}):
|
||||||
"""
|
"""
|
||||||
Creates a PDF from a LaTex templates using pdflatex.
|
Creates a PDF from a LaTex templates using pdflatex.
|
||||||
Writes it in a temporary directory and send back an HTTP response for
|
Writes it in a temporary directory and send back an HTTP response for
|
||||||
|
@ -66,13 +70,13 @@ def render_tex(request, template, ctx={}):
|
||||||
context = Context(ctx)
|
context = Context(ctx)
|
||||||
template = get_template(template)
|
template = get_template(template)
|
||||||
rendered_tpl = template.render(context).encode('utf-8')
|
rendered_tpl = template.render(context).encode('utf-8')
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tempdir:
|
with tempfile.TemporaryDirectory() as tempdir:
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
process = Popen(
|
process = Popen(
|
||||||
['pdflatex', '-output-directory', tempdir],
|
['pdflatex', '-output-directory', tempdir],
|
||||||
stdin = PIPE,
|
stdin=PIPE,
|
||||||
stdout = PIPE,
|
stdout=PIPE,
|
||||||
)
|
)
|
||||||
process.communicate(rendered_tpl)
|
process.communicate(rendered_tpl)
|
||||||
with open(os.path.join(tempdir, 'texput.pdf'), 'rb') as f:
|
with open(os.path.join(tempdir, 'texput.pdf'), 'rb') as f:
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
# 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.
|
||||||
|
"""cotisations.urls
|
||||||
|
The defined URLs for the Cotisations app
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
@ -29,107 +32,131 @@ from . import views
|
||||||
from . import payment
|
from . import payment
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^new_facture/(?P<userid>[0-9]+)$',
|
url(
|
||||||
|
r'^new_facture/(?P<userid>[0-9]+)$',
|
||||||
views.new_facture,
|
views.new_facture,
|
||||||
name='new-facture'
|
name='new-facture'
|
||||||
),
|
),
|
||||||
url(r'^edit_facture/(?P<factureid>[0-9]+)$',
|
url(
|
||||||
|
r'^edit_facture/(?P<factureid>[0-9]+)$',
|
||||||
views.edit_facture,
|
views.edit_facture,
|
||||||
name='edit-facture'
|
name='edit-facture'
|
||||||
),
|
),
|
||||||
url(r'^del_facture/(?P<factureid>[0-9]+)$',
|
url(
|
||||||
|
r'^del_facture/(?P<factureid>[0-9]+)$',
|
||||||
views.del_facture,
|
views.del_facture,
|
||||||
name='del-facture'
|
name='del-facture'
|
||||||
),
|
),
|
||||||
url(r'^facture_pdf/(?P<factureid>[0-9]+)$',
|
url(
|
||||||
|
r'^facture_pdf/(?P<factureid>[0-9]+)$',
|
||||||
views.facture_pdf,
|
views.facture_pdf,
|
||||||
name='facture-pdf'
|
name='facture-pdf'
|
||||||
),
|
),
|
||||||
url(r'^new_facture_pdf/$',
|
url(
|
||||||
|
r'^new_facture_pdf/$',
|
||||||
views.new_facture_pdf,
|
views.new_facture_pdf,
|
||||||
name='new-facture-pdf'
|
name='new-facture-pdf'
|
||||||
),
|
),
|
||||||
url(r'^credit_solde/(?P<userid>[0-9]+)$',
|
url(
|
||||||
|
r'^credit_solde/(?P<userid>[0-9]+)$',
|
||||||
views.credit_solde,
|
views.credit_solde,
|
||||||
name='credit-solde'
|
name='credit-solde'
|
||||||
),
|
),
|
||||||
url(r'^add_article/$',
|
url(
|
||||||
|
r'^add_article/$',
|
||||||
views.add_article,
|
views.add_article,
|
||||||
name='add-article'
|
name='add-article'
|
||||||
),
|
),
|
||||||
url(r'^edit_article/(?P<articleid>[0-9]+)$',
|
url(
|
||||||
|
r'^edit_article/(?P<articleid>[0-9]+)$',
|
||||||
views.edit_article,
|
views.edit_article,
|
||||||
name='edit-article'
|
name='edit-article'
|
||||||
),
|
),
|
||||||
url(r'^del_article/$',
|
url(
|
||||||
|
r'^del_article/$',
|
||||||
views.del_article,
|
views.del_article,
|
||||||
name='del-article'
|
name='del-article'
|
||||||
),
|
),
|
||||||
url(r'^add_paiement/$',
|
url(
|
||||||
|
r'^add_paiement/$',
|
||||||
views.add_paiement,
|
views.add_paiement,
|
||||||
name='add-paiement'
|
name='add-paiement'
|
||||||
),
|
),
|
||||||
url(r'^edit_paiement/(?P<paiementid>[0-9]+)$',
|
url(
|
||||||
|
r'^edit_paiement/(?P<paiementid>[0-9]+)$',
|
||||||
views.edit_paiement,
|
views.edit_paiement,
|
||||||
name='edit-paiement'
|
name='edit-paiement'
|
||||||
),
|
),
|
||||||
url(r'^del_paiement/$',
|
url(
|
||||||
|
r'^del_paiement/$',
|
||||||
views.del_paiement,
|
views.del_paiement,
|
||||||
name='del-paiement'
|
name='del-paiement'
|
||||||
),
|
),
|
||||||
url(r'^add_banque/$',
|
url(
|
||||||
|
r'^add_banque/$',
|
||||||
views.add_banque,
|
views.add_banque,
|
||||||
name='add-banque'
|
name='add-banque'
|
||||||
),
|
),
|
||||||
url(r'^edit_banque/(?P<banqueid>[0-9]+)$',
|
url(
|
||||||
|
r'^edit_banque/(?P<banqueid>[0-9]+)$',
|
||||||
views.edit_banque,
|
views.edit_banque,
|
||||||
name='edit-banque'
|
name='edit-banque'
|
||||||
),
|
),
|
||||||
url(r'^del_banque/$',
|
url(
|
||||||
|
r'^del_banque/$',
|
||||||
views.del_banque,
|
views.del_banque,
|
||||||
name='del-banque'
|
name='del-banque'
|
||||||
),
|
),
|
||||||
url(r'^index_article/$',
|
url(
|
||||||
|
r'^index_article/$',
|
||||||
views.index_article,
|
views.index_article,
|
||||||
name='index-article'
|
name='index-article'
|
||||||
),
|
),
|
||||||
url(r'^index_banque/$',
|
url(
|
||||||
|
r'^index_banque/$',
|
||||||
views.index_banque,
|
views.index_banque,
|
||||||
name='index-banque'
|
name='index-banque'
|
||||||
),
|
),
|
||||||
url(r'^index_paiement/$',
|
url(
|
||||||
|
r'^index_paiement/$',
|
||||||
views.index_paiement,
|
views.index_paiement,
|
||||||
name='index-paiement'
|
name='index-paiement'
|
||||||
),
|
),
|
||||||
url(
|
url(
|
||||||
r'history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
r'history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
||||||
re2o.views.history,
|
re2o.views.history,
|
||||||
name='history',
|
name='history',
|
||||||
kwargs={'application':'cotisations'},
|
kwargs={'application': 'cotisations'},
|
||||||
),
|
),
|
||||||
url(r'^control/$',
|
url(
|
||||||
|
r'^control/$',
|
||||||
views.control,
|
views.control,
|
||||||
name='control'
|
name='control'
|
||||||
),
|
),
|
||||||
url(r'^new_facture_solde/(?P<userid>[0-9]+)$',
|
url(
|
||||||
|
r'^new_facture_solde/(?P<userid>[0-9]+)$',
|
||||||
views.new_facture_solde,
|
views.new_facture_solde,
|
||||||
name='new_facture_solde'
|
name='new_facture_solde'
|
||||||
),
|
),
|
||||||
url(r'^recharge/$',
|
url(
|
||||||
|
r'^recharge/$',
|
||||||
views.recharge,
|
views.recharge,
|
||||||
name='recharge'
|
name='recharge'
|
||||||
),
|
),
|
||||||
url(r'^payment/accept/(?P<factureid>[0-9]+)$',
|
url(
|
||||||
|
r'^payment/accept/(?P<factureid>[0-9]+)$',
|
||||||
payment.accept_payment,
|
payment.accept_payment,
|
||||||
name='accept_payment'
|
name='accept_payment'
|
||||||
),
|
),
|
||||||
url(r'^payment/refuse/$',
|
url(
|
||||||
|
r'^payment/refuse/$',
|
||||||
payment.refuse_payment,
|
payment.refuse_payment,
|
||||||
name='refuse_payment'
|
name='refuse_payment'
|
||||||
),
|
),
|
||||||
url(r'^payment/ipn/$',
|
url(
|
||||||
|
r'^payment/ipn/$',
|
||||||
payment.ipn,
|
payment.ipn,
|
||||||
name='ipn'
|
name='ipn'
|
||||||
),
|
),
|
||||||
url(r'^$', views.index, name='index'),
|
url(r'^$', views.index, name='index'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -23,22 +23,23 @@
|
||||||
# App de gestion des users pour re2o
|
# App de gestion des users pour re2o
|
||||||
# Goulven Kermarec, Gabriel Détraz
|
# Goulven Kermarec, Gabriel Détraz
|
||||||
# Gplv2
|
# Gplv2
|
||||||
|
"""cotisations.views
|
||||||
|
The different views used in the Cotisations module
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.core.validators import MaxValueValidator
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.db.models import ProtectedError
|
from django.db.models import ProtectedError
|
||||||
from django.db import transaction
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.forms import modelformset_factory, formset_factory
|
from django.forms import modelformset_factory, formset_factory
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
|
||||||
from django.views.decorators.debug import sensitive_variables
|
|
||||||
# Import des models, forms et fonctions re2o
|
# Import des models, forms et fonctions re2o
|
||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
from users.models import User
|
from users.models import User
|
||||||
|
@ -70,14 +71,12 @@ from .forms import (
|
||||||
SelectUserArticleForm,
|
SelectUserArticleForm,
|
||||||
SelectClubArticleForm,
|
SelectClubArticleForm,
|
||||||
CreditSoldeForm,
|
CreditSoldeForm,
|
||||||
NewFactureSoldeForm,
|
|
||||||
RechargeForm
|
RechargeForm
|
||||||
)
|
)
|
||||||
from . import payment as online_payment
|
from . import payment as online_payment
|
||||||
from .tex import render_invoice
|
from .tex import render_invoice
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_create(Facture)
|
@can_create(Facture)
|
||||||
@can_edit(User)
|
@can_edit(User)
|
||||||
|
@ -102,9 +101,13 @@ def new_facture(request, user, userid):
|
||||||
# Building the invocie form and the article formset
|
# Building the invocie form and the article formset
|
||||||
invoice_form = NewFactureForm(request.POST or None, instance=invoice)
|
invoice_form = NewFactureForm(request.POST or None, instance=invoice)
|
||||||
if request.user.is_class_club:
|
if request.user.is_class_club:
|
||||||
article_formset = formset_factory(SelectClubArticleForm)(request.POST or None)
|
article_formset = formset_factory(SelectClubArticleForm)(
|
||||||
|
request.POST or None
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
article_formset = formset_factory(SelectUserArticleForm)(request.POST or None)
|
article_formset = formset_factory(SelectUserArticleForm)(
|
||||||
|
request.POST or None
|
||||||
|
)
|
||||||
|
|
||||||
if invoice_form.is_valid() and article_formset.is_valid():
|
if invoice_form.is_valid() and article_formset.is_valid():
|
||||||
new_invoice_instance = invoice_form.save(commit=False)
|
new_invoice_instance = invoice_form.save(commit=False)
|
||||||
|
@ -118,15 +121,18 @@ def new_facture(request, user, userid):
|
||||||
# the authorized minimum (negative_balance)
|
# the authorized minimum (negative_balance)
|
||||||
if user_balance:
|
if user_balance:
|
||||||
# TODO : change Paiement to Payment
|
# TODO : change Paiement to Payment
|
||||||
if new_invoice_instance.paiement == Paiement.objects.get_or_create(
|
if new_invoice_instance.paiement == (
|
||||||
moyen='solde'
|
Paiement.objects.get_or_create(moyen='solde')[0]
|
||||||
)[0]:
|
):
|
||||||
total_price = 0
|
total_price = 0
|
||||||
for art_item in articles:
|
for art_item in articles:
|
||||||
if art_item.cleaned_data:
|
if art_item.cleaned_data:
|
||||||
total_price += art_item.cleaned_data['article']\
|
total_price += (
|
||||||
.prix*art_item.cleaned_data['quantity']
|
art_item.cleaned_data['article'].prix *
|
||||||
if float(user.solde) - float(total_price) < negative_balance:
|
art_item.cleaned_data['quantity']
|
||||||
|
)
|
||||||
|
if (float(user.solde) - float(total_price)
|
||||||
|
< negative_balance):
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
_("Your balance is too low for this operation.")
|
_("Your balance is too low for this operation.")
|
||||||
|
@ -194,7 +200,7 @@ def new_facture(request, user, userid):
|
||||||
@can_change(Facture, 'pdf')
|
@can_change(Facture, 'pdf')
|
||||||
def new_facture_pdf(request):
|
def new_facture_pdf(request):
|
||||||
"""
|
"""
|
||||||
View used to generate a custom PDF invoice. It's mainly used to
|
View used to generate a custom PDF invoice. It's mainly used to
|
||||||
get invoices that are not taken into account, for the administrative
|
get invoices that are not taken into account, for the administrative
|
||||||
point of view.
|
point of view.
|
||||||
"""
|
"""
|
||||||
|
@ -205,9 +211,13 @@ def new_facture_pdf(request):
|
||||||
# Building the invocie form and the article formset
|
# Building the invocie form and the article formset
|
||||||
invoice_form = NewFactureFormPdf(request.POST or None)
|
invoice_form = NewFactureFormPdf(request.POST or None)
|
||||||
if request.user.is_class_club:
|
if request.user.is_class_club:
|
||||||
articles_formset = formset_factory(SelectClubArticleForm)(request.POST or None)
|
articles_formset = formset_factory(SelectClubArticleForm)(
|
||||||
|
request.POST or None
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
articles_formset = formset_factory(SelectUserArticleForm)(request.POST or None)
|
articles_formset = formset_factory(SelectUserArticleForm)(
|
||||||
|
request.POST or None
|
||||||
|
)
|
||||||
if invoice_form.is_valid() and articles_formset.is_valid():
|
if invoice_form.is_valid() and articles_formset.is_valid():
|
||||||
# Get the article list and build an list out of it
|
# Get the article list and build an list out of it
|
||||||
# contiaining (article_name, article_price, quantity, total_price)
|
# contiaining (article_name, article_price, quantity, total_price)
|
||||||
|
@ -253,7 +263,7 @@ def new_facture_pdf(request):
|
||||||
# TODO : change facture to invoice
|
# TODO : change facture to invoice
|
||||||
@login_required
|
@login_required
|
||||||
@can_view(Facture)
|
@can_view(Facture)
|
||||||
def facture_pdf(request, facture, factureid):
|
def facture_pdf(request, facture, **_kwargs):
|
||||||
"""
|
"""
|
||||||
View used to generate a PDF file from an existing invoice in database
|
View used to generate a PDF file from an existing invoice in database
|
||||||
Creates a line for each Purchase (thus article sold) and generate the
|
Creates a line for each Purchase (thus article sold) and generate the
|
||||||
|
@ -296,14 +306,18 @@ def facture_pdf(request, facture, factureid):
|
||||||
# TODO : change facture to invoice
|
# TODO : change facture to invoice
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Facture)
|
@can_edit(Facture)
|
||||||
def edit_facture(request, facture, factureid):
|
def edit_facture(request, facture, **_kwargs):
|
||||||
"""
|
"""
|
||||||
View used to edit an existing invoice.
|
View used to edit an existing invoice.
|
||||||
Articles can be added or remove to the invoice and quantity
|
Articles can be added or remove to the invoice and quantity
|
||||||
can be set as desired. This is also the view used to invalidate
|
can be set as desired. This is also the view used to invalidate
|
||||||
an invoice.
|
an invoice.
|
||||||
"""
|
"""
|
||||||
invoice_form = EditFactureForm(request.POST or None, instance=facture, user=request.user)
|
invoice_form = EditFactureForm(
|
||||||
|
request.POST or None,
|
||||||
|
instance=facture,
|
||||||
|
user=request.user
|
||||||
|
)
|
||||||
purchases_objects = Vente.objects.filter(facture=facture)
|
purchases_objects = Vente.objects.filter(facture=facture)
|
||||||
purchase_form_set = modelformset_factory(
|
purchase_form_set = modelformset_factory(
|
||||||
Vente,
|
Vente,
|
||||||
|
@ -311,7 +325,10 @@ def edit_facture(request, facture, factureid):
|
||||||
extra=0,
|
extra=0,
|
||||||
max_num=len(purchases_objects)
|
max_num=len(purchases_objects)
|
||||||
)
|
)
|
||||||
purchase_form = purchase_form_set(request.POST or None, queryset=purchases_objects)
|
purchase_form = purchase_form_set(
|
||||||
|
request.POST or None,
|
||||||
|
queryset=purchases_objects
|
||||||
|
)
|
||||||
if invoice_form.is_valid() and purchase_form.is_valid():
|
if invoice_form.is_valid() and purchase_form.is_valid():
|
||||||
if invoice_form.changed_data:
|
if invoice_form.changed_data:
|
||||||
invoice_form.save()
|
invoice_form.save()
|
||||||
|
@ -330,7 +347,7 @@ def edit_facture(request, facture, factureid):
|
||||||
# TODO : change facture to invoice
|
# TODO : change facture to invoice
|
||||||
@login_required
|
@login_required
|
||||||
@can_delete(Facture)
|
@can_delete(Facture)
|
||||||
def del_facture(request, facture, factureid):
|
def del_facture(request, facture, **_kwargs):
|
||||||
"""
|
"""
|
||||||
View used to delete an existing invocie.
|
View used to delete an existing invocie.
|
||||||
"""
|
"""
|
||||||
|
@ -351,7 +368,7 @@ def del_facture(request, facture, factureid):
|
||||||
@login_required
|
@login_required
|
||||||
@can_create(Facture)
|
@can_create(Facture)
|
||||||
@can_edit(User)
|
@can_edit(User)
|
||||||
def credit_solde(request, user, userid):
|
def credit_solde(request, user, **_kwargs):
|
||||||
"""
|
"""
|
||||||
View used to edit the balance of a user.
|
View used to edit the balance of a user.
|
||||||
Can be use either to increase or decrease a user's balance.
|
Can be use either to increase or decrease a user's balance.
|
||||||
|
@ -385,7 +402,7 @@ def credit_solde(request, user, userid):
|
||||||
def add_article(request):
|
def add_article(request):
|
||||||
"""
|
"""
|
||||||
View used to add an article.
|
View used to add an article.
|
||||||
|
|
||||||
.. note:: If a purchase has already been sold, the price are calculated
|
.. note:: If a purchase has already been sold, the price are calculated
|
||||||
once and for all. That means even if the price of an article is edited
|
once and for all. That means even if the price of an article is edited
|
||||||
later, it won't change the invoice. That is really important to keep
|
later, it won't change the invoice. That is really important to keep
|
||||||
|
@ -408,7 +425,7 @@ def add_article(request):
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Article)
|
@can_edit(Article)
|
||||||
def edit_article(request, article_instance, articleid):
|
def edit_article(request, article_instance, **_kwargs):
|
||||||
"""
|
"""
|
||||||
View used to edit an article.
|
View used to edit an article.
|
||||||
"""
|
"""
|
||||||
|
@ -472,7 +489,7 @@ def add_paiement(request):
|
||||||
# TODO : chnage paiement to Payment
|
# TODO : chnage paiement to Payment
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Paiement)
|
@can_edit(Paiement)
|
||||||
def edit_paiement(request, paiement_instance, paiementid):
|
def edit_paiement(request, paiement_instance, **_kwargs):
|
||||||
"""
|
"""
|
||||||
View used to edit a payment method.
|
View used to edit a payment method.
|
||||||
"""
|
"""
|
||||||
|
@ -550,7 +567,7 @@ def add_banque(request):
|
||||||
# TODO : change banque to bank
|
# TODO : change banque to bank
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Banque)
|
@can_edit(Banque)
|
||||||
def edit_banque(request, banque_instance, banqueid):
|
def edit_banque(request, banque_instance, **_kwargs):
|
||||||
"""
|
"""
|
||||||
View used to edit a bank.
|
View used to edit a bank.
|
||||||
"""
|
"""
|
||||||
|
@ -613,7 +630,8 @@ def control(request):
|
||||||
View used to control the invoices all at once.
|
View used to control the invoices all at once.
|
||||||
"""
|
"""
|
||||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||||
invoice_list = Facture.objects.select_related('user').select_related('paiement')
|
invoice_list = (Facture.objects.select_related('user').
|
||||||
|
select_related('paiement'))
|
||||||
invoice_list = SortTable.sort(
|
invoice_list = SortTable.sort(
|
||||||
invoice_list,
|
invoice_list,
|
||||||
request.GET.get('col'),
|
request.GET.get('col'),
|
||||||
|
@ -725,9 +743,13 @@ def new_facture_solde(request, userid):
|
||||||
Q(type_user='All') | Q(type_user=request.user.class_name)
|
Q(type_user='All') | Q(type_user=request.user.class_name)
|
||||||
)
|
)
|
||||||
if request.user.is_class_club:
|
if request.user.is_class_club:
|
||||||
article_formset = formset_factory(SelectClubArticleForm)(request.POST or None)
|
article_formset = formset_factory(SelectClubArticleForm)(
|
||||||
|
request.POST or None
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
article_formset = formset_factory(SelectUserArticleForm)(request.POST or None)
|
article_formset = formset_factory(SelectUserArticleForm)(
|
||||||
|
request.POST or None
|
||||||
|
)
|
||||||
|
|
||||||
if article_formset.is_valid():
|
if article_formset.is_valid():
|
||||||
articles = article_formset
|
articles = article_formset
|
||||||
|
@ -826,7 +848,9 @@ def recharge(request):
|
||||||
refill_form = RechargeForm(request.POST or None, user=request.user)
|
refill_form = RechargeForm(request.POST or None, user=request.user)
|
||||||
if refill_form.is_valid():
|
if refill_form.is_valid():
|
||||||
invoice = Facture(user=request.user)
|
invoice = Facture(user=request.user)
|
||||||
payment, _created = Paiement.objects.get_or_create(moyen='Rechargement en ligne')
|
payment, _created = Paiement.objects.get_or_create(
|
||||||
|
moyen='Rechargement en ligne'
|
||||||
|
)
|
||||||
invoice.paiement = payment
|
invoice.paiement = payment
|
||||||
invoice.valid = False
|
invoice.valid = False
|
||||||
invoice.save()
|
invoice.save()
|
||||||
|
@ -837,7 +861,9 @@ def recharge(request):
|
||||||
number=1
|
number=1
|
||||||
)
|
)
|
||||||
purchase.save()
|
purchase.save()
|
||||||
content = online_payment.PAYMENT_SYSTEM[AssoOption.get_cached_value('payment')](invoice, request)
|
content = online_payment.PAYMENT_SYSTEM[
|
||||||
|
AssoOption.get_cached_value('payment')
|
||||||
|
](invoice, request)
|
||||||
return render(request, 'cotisations/payment.html', content)
|
return render(request, 'cotisations/payment.html', content)
|
||||||
return form({
|
return form({
|
||||||
'rechargeform': refill_form
|
'rechargeform': refill_form
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# ⁻*- mode: python; coding: utf-8 -*-
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
# 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
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
# quelques clics.
|
# quelques clics.
|
||||||
|
@ -35,13 +35,18 @@ https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_pyth
|
||||||
Inspiré du travail de Daniel Stan au Crans
|
Inspiré du travail de Daniel Stan au Crans
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
import logging
|
import logging
|
||||||
import netaddr
|
import radiusd # Module magique freeradius (radiusd.py is dummy)
|
||||||
import radiusd # Module magique freeradius (radiusd.py is dummy)
|
|
||||||
import binascii
|
|
||||||
import hashlib
|
|
||||||
import os, sys
|
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from machines.models import Interface, IpList, Nas, Domain
|
||||||
|
from topologie.models import Port, Switch
|
||||||
|
from users.models import User
|
||||||
|
from preferences.models import OptionalTopologie
|
||||||
|
|
||||||
proj_path = "/var/www/re2o/"
|
proj_path = "/var/www/re2o/"
|
||||||
# This is so Django knows where to find stuff.
|
# This is so Django knows where to find stuff.
|
||||||
|
@ -52,28 +57,17 @@ sys.path.append(proj_path)
|
||||||
os.chdir(proj_path)
|
os.chdir(proj_path)
|
||||||
|
|
||||||
# This is so models get loaded.
|
# This is so models get loaded.
|
||||||
from django.core.wsgi import get_wsgi_application
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
from django.db.models import Q
|
|
||||||
from machines.models import Interface, IpList, Nas, Domain
|
|
||||||
from topologie.models import Room, Port, Switch
|
|
||||||
from users.models import User
|
|
||||||
from preferences.models import OptionalTopologie
|
|
||||||
|
|
||||||
options, created = OptionalTopologie.objects.get_or_create()
|
options, created = OptionalTopologie.objects.get_or_create()
|
||||||
VLAN_NOK = options.vlan_decision_nok.vlan_id
|
VLAN_NOK = options.vlan_decision_nok.vlan_id
|
||||||
VLAN_OK = options.vlan_decision_ok.vlan_id
|
VLAN_OK = options.vlan_decision_ok.vlan_id
|
||||||
|
|
||||||
|
|
||||||
#: Serveur radius de test (pas la prod)
|
#: Serveur radius de test (pas la prod)
|
||||||
TEST_SERVER = bool(os.getenv('DBG_FREERADIUS', False))
|
TEST_SERVER = bool(os.getenv('DBG_FREERADIUS', False))
|
||||||
|
|
||||||
|
|
||||||
## -*- Logging -*-
|
# Logging
|
||||||
|
|
||||||
class RadiusdHandler(logging.Handler):
|
class RadiusdHandler(logging.Handler):
|
||||||
"""Handler de logs pour freeradius"""
|
"""Handler de logs pour freeradius"""
|
||||||
|
|
||||||
|
@ -87,6 +81,7 @@ class RadiusdHandler(logging.Handler):
|
||||||
rad_sig = radiusd.L_DBG
|
rad_sig = radiusd.L_DBG
|
||||||
radiusd.radlog(rad_sig, record.msg)
|
radiusd.radlog(rad_sig, record.msg)
|
||||||
|
|
||||||
|
|
||||||
# Initialisation d'un logger (pour logguer unifié)
|
# Initialisation d'un logger (pour logguer unifié)
|
||||||
logger = logging.getLogger('auth.py')
|
logger = logging.getLogger('auth.py')
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
|
@ -95,10 +90,11 @@ handler = RadiusdHandler()
|
||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
logger.addHandler(handler)
|
logger.addHandler(handler)
|
||||||
|
|
||||||
|
|
||||||
def radius_event(fun):
|
def radius_event(fun):
|
||||||
"""Décorateur pour les fonctions d'interfaces avec radius.
|
"""Décorateur pour les fonctions d'interfaces avec radius.
|
||||||
Une telle fonction prend un uniquement argument, qui est une liste de tuples
|
Une telle fonction prend un uniquement argument, qui est une liste de
|
||||||
(clé, valeur) et renvoie un triplet dont les composantes sont :
|
tuples (clé, valeur) et renvoie un triplet dont les composantes sont :
|
||||||
* le code de retour (voir radiusd.RLM_MODULE_* )
|
* le code de retour (voir radiusd.RLM_MODULE_* )
|
||||||
* un tuple de couples (clé, valeur) pour les valeurs de réponse (accès ok
|
* un tuple de couples (clé, valeur) pour les valeurs de réponse (accès ok
|
||||||
et autres trucs du genre)
|
et autres trucs du genre)
|
||||||
|
@ -109,7 +105,8 @@ def radius_event(fun):
|
||||||
tuples en entrée en un dictionnaire."""
|
tuples en entrée en un dictionnaire."""
|
||||||
|
|
||||||
def new_f(auth_data):
|
def new_f(auth_data):
|
||||||
if type(auth_data) == dict:
|
""" The function transforming the tuples as dict """
|
||||||
|
if isinstance(auth_data, dict):
|
||||||
data = auth_data
|
data = auth_data
|
||||||
else:
|
else:
|
||||||
data = dict()
|
data = dict()
|
||||||
|
@ -118,8 +115,8 @@ def radius_event(fun):
|
||||||
# Ex: Calling-Station-Id: "une_adresse_mac"
|
# Ex: Calling-Station-Id: "une_adresse_mac"
|
||||||
data[key] = value.replace('"', '')
|
data[key] = value.replace('"', '')
|
||||||
try:
|
try:
|
||||||
# TODO s'assurer ici que les tuples renvoyés sont bien des (str,str)
|
# TODO s'assurer ici que les tuples renvoyés sont bien des
|
||||||
# rlm_python ne digère PAS les unicodes
|
# (str,str) : rlm_python ne digère PAS les unicodes
|
||||||
return fun(data)
|
return fun(data)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logger.error('Failed %r on data %r' % (err, auth_data))
|
logger.error('Failed %r on data %r' % (err, auth_data))
|
||||||
|
@ -128,7 +125,6 @@ def radius_event(fun):
|
||||||
return new_f
|
return new_f
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@radius_event
|
@radius_event
|
||||||
def instantiate(*_):
|
def instantiate(*_):
|
||||||
"""Utile pour initialiser les connexions ldap une première fois (otherwise,
|
"""Utile pour initialiser les connexions ldap une première fois (otherwise,
|
||||||
|
@ -137,12 +133,15 @@ def instantiate(*_):
|
||||||
if TEST_SERVER:
|
if TEST_SERVER:
|
||||||
logger.info(u'DBG_FREERADIUS is enabled')
|
logger.info(u'DBG_FREERADIUS is enabled')
|
||||||
|
|
||||||
|
|
||||||
@radius_event
|
@radius_event
|
||||||
def authorize(data):
|
def authorize(data):
|
||||||
"""On test si on connait le calling nas:
|
"""On test si on connait le calling nas:
|
||||||
- si le nas est inconnue, on suppose que c'est une requète 802.1X, on la traite
|
- si le nas est inconnue, on suppose que c'est une requète 802.1X, on la
|
||||||
|
traite
|
||||||
- si le nas est connu, on applique 802.1X si le mode est activé
|
- si le nas est connu, on applique 802.1X si le mode est activé
|
||||||
- si le nas est connu et si il s'agit d'un nas auth par mac, on repond accept en authorize
|
- si le nas est connu et si il s'agit d'un nas auth par mac, on repond
|
||||||
|
accept en authorize
|
||||||
"""
|
"""
|
||||||
# Pour les requetes proxifiees, on split
|
# Pour les requetes proxifiees, on split
|
||||||
nas = data.get('NAS-IP-Address', data.get('NAS-Identifier', None))
|
nas = data.get('NAS-IP-Address', data.get('NAS-Identifier', None))
|
||||||
|
@ -155,30 +154,40 @@ def authorize(data):
|
||||||
user = data.get('User-Name', '').decode('utf-8', errors='replace')
|
user = data.get('User-Name', '').decode('utf-8', errors='replace')
|
||||||
user = user.split('@', 1)[0]
|
user = user.split('@', 1)[0]
|
||||||
mac = data.get('Calling-Station-Id', '')
|
mac = data.get('Calling-Station-Id', '')
|
||||||
result, log, password = check_user_machine_and_register(nas_type, user, mac)
|
result, log, password = check_user_machine_and_register(
|
||||||
|
nas_type,
|
||||||
|
user,
|
||||||
|
mac
|
||||||
|
)
|
||||||
logger.info(log.encode('utf-8'))
|
logger.info(log.encode('utf-8'))
|
||||||
logger.info(user.encode('utf-8'))
|
logger.info(user.encode('utf-8'))
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
return radiusd.RLM_MODULE_REJECT
|
return radiusd.RLM_MODULE_REJECT
|
||||||
else:
|
else:
|
||||||
return (radiusd.RLM_MODULE_UPDATED,
|
return (
|
||||||
(),
|
radiusd.RLM_MODULE_UPDATED,
|
||||||
(
|
(),
|
||||||
(str("NT-Password"), str(password)),
|
(
|
||||||
),
|
(str("NT-Password"), str(password)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return (radiusd.RLM_MODULE_UPDATED,
|
return (
|
||||||
(),
|
radiusd.RLM_MODULE_UPDATED,
|
||||||
(
|
(),
|
||||||
("Auth-Type", "Accept"),
|
(
|
||||||
),
|
("Auth-Type", "Accept"),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@radius_event
|
@radius_event
|
||||||
def post_auth(data):
|
def post_auth(data):
|
||||||
|
""" Function called after the user is authenticated
|
||||||
|
"""
|
||||||
|
|
||||||
nas = data.get('NAS-IP-Address', data.get('NAS-Identifier', None))
|
nas = data.get('NAS-IP-Address', data.get('NAS-Identifier', None))
|
||||||
nas_instance = find_nas_from_request(nas)
|
nas_instance = find_nas_from_request(nas)
|
||||||
# Toutes les reuquètes non proxifiées
|
# Toutes les reuquètes non proxifiées
|
||||||
|
@ -187,61 +196,89 @@ def post_auth(data):
|
||||||
return radiusd.RLM_MODULE_OK
|
return radiusd.RLM_MODULE_OK
|
||||||
nas_type = Nas.objects.filter(nas_type=nas_instance.type).first()
|
nas_type = Nas.objects.filter(nas_type=nas_instance.type).first()
|
||||||
if not nas_type:
|
if not nas_type:
|
||||||
logger.info(u"Type de nas non enregistré dans la bdd!".encode('utf-8'))
|
logger.info(
|
||||||
|
u"Type de nas non enregistré dans la bdd!".encode('utf-8')
|
||||||
|
)
|
||||||
return radiusd.RLM_MODULE_OK
|
return radiusd.RLM_MODULE_OK
|
||||||
|
|
||||||
mac = data.get('Calling-Station-Id', None)
|
mac = data.get('Calling-Station-Id', None)
|
||||||
|
|
||||||
# Switch et bornes héritent de machine et peuvent avoir plusieurs interfaces filles
|
# Switch et bornes héritent de machine et peuvent avoir plusieurs
|
||||||
|
# interfaces filles
|
||||||
nas_machine = nas_instance.machine
|
nas_machine = nas_instance.machine
|
||||||
# Si il s'agit d'un switch
|
# Si il s'agit d'un switch
|
||||||
if hasattr(nas_machine, 'switch'):
|
if hasattr(nas_machine, 'switch'):
|
||||||
port = data.get('NAS-Port-Id', data.get('NAS-Port', None))
|
port = data.get('NAS-Port-Id', data.get('NAS-Port', None))
|
||||||
#Pour les infrastructures possédant des switchs Juniper :
|
# Pour les infrastructures possédant des switchs Juniper :
|
||||||
#On vérifie si le switch fait partie d'un stack Juniper
|
# On vérifie si le switch fait partie d'un stack Juniper
|
||||||
instance_stack = nas_machine.switch.stack
|
instance_stack = nas_machine.switch.stack
|
||||||
if instance_stack:
|
if instance_stack:
|
||||||
# Si c'est le cas, on resélectionne le bon switch dans la stack
|
# Si c'est le cas, on resélectionne le bon switch dans la stack
|
||||||
id_stack_member = port.split("-")[1].split('/')[0]
|
id_stack_member = port.split("-")[1].split('/')[0]
|
||||||
nas_machine = Switch.objects.filter(stack=instance_stack).filter(stack_member_id=id_stack_member).prefetch_related('interface_set__domain__extension').first()
|
nas_machine = (Switch.objects
|
||||||
# On récupère le numéro du port sur l'output de freeradius. La ligne suivante fonctionne pour cisco, HP et Juniper
|
.filter(stack=instance_stack)
|
||||||
|
.filter(stack_member_id=id_stack_member)
|
||||||
|
.prefetch_related(
|
||||||
|
'interface_set__domain__extension'
|
||||||
|
)
|
||||||
|
.first())
|
||||||
|
# On récupère le numéro du port sur l'output de freeradius.
|
||||||
|
# La ligne suivante fonctionne pour cisco, HP et Juniper
|
||||||
port = port.split(".")[0].split('/')[-1][-2:]
|
port = port.split(".")[0].split('/')[-1][-2:]
|
||||||
out = decide_vlan_and_register_switch(nas_machine, nas_type, port, mac)
|
out = decide_vlan_and_register_switch(nas_machine, nas_type, port, mac)
|
||||||
sw_name, room, reason, vlan_id = out
|
sw_name, room, reason, vlan_id = out
|
||||||
|
|
||||||
log_message = '(fil) %s -> %s [%s%s]' % \
|
log_message = '(fil) %s -> %s [%s%s]' % (
|
||||||
(sw_name + u":" + port + u"/" + unicode(room), mac, vlan_id, (reason and u': ' + reason).encode('utf-8'))
|
sw_name + u":" + port + u"/" + str(room),
|
||||||
|
mac,
|
||||||
|
vlan_id,
|
||||||
|
(reason and u': ' + reason).encode('utf-8')
|
||||||
|
)
|
||||||
logger.info(log_message)
|
logger.info(log_message)
|
||||||
|
|
||||||
# Filaire
|
# Filaire
|
||||||
return (radiusd.RLM_MODULE_UPDATED,
|
return (
|
||||||
|
radiusd.RLM_MODULE_UPDATED,
|
||||||
(
|
(
|
||||||
("Tunnel-Type", "VLAN"),
|
("Tunnel-Type", "VLAN"),
|
||||||
("Tunnel-Medium-Type", "IEEE-802"),
|
("Tunnel-Medium-Type", "IEEE-802"),
|
||||||
("Tunnel-Private-Group-Id", '%d' % int(vlan_id)),
|
("Tunnel-Private-Group-Id", '%d' % int(vlan_id)),
|
||||||
),
|
),
|
||||||
()
|
()
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return radiusd.RLM_MODULE_OK
|
return radiusd.RLM_MODULE_OK
|
||||||
|
|
||||||
|
|
||||||
|
# TODO : remove this function
|
||||||
@radius_event
|
@radius_event
|
||||||
def dummy_fun(_):
|
def dummy_fun(_):
|
||||||
"""Do nothing, successfully. (C'est pour avoir un truc à mettre)"""
|
"""Do nothing, successfully. (C'est pour avoir un truc à mettre)"""
|
||||||
return radiusd.RLM_MODULE_OK
|
return radiusd.RLM_MODULE_OK
|
||||||
|
|
||||||
|
|
||||||
def detach(_=None):
|
def detach(_=None):
|
||||||
"""Appelé lors du déchargement du module (enfin, normalement)"""
|
"""Appelé lors du déchargement du module (enfin, normalement)"""
|
||||||
print "*** goodbye from auth.py ***"
|
print("*** goodbye from auth.py ***")
|
||||||
return radiusd.RLM_MODULE_OK
|
return radiusd.RLM_MODULE_OK
|
||||||
|
|
||||||
|
|
||||||
def find_nas_from_request(nas_id):
|
def find_nas_from_request(nas_id):
|
||||||
nas = Interface.objects.filter(Q(domain=Domain.objects.filter(name=nas_id)) | Q(ipv4=IpList.objects.filter(ipv4=nas_id))).select_related('type').select_related('machine__switch__stack')
|
""" Get the nas object from its ID """
|
||||||
|
nas = (Interface.objects
|
||||||
|
.filter(
|
||||||
|
Q(domain=Domain.objects.filter(name=nas_id)) |
|
||||||
|
Q(ipv4=IpList.objects.filter(ipv4=nas_id))
|
||||||
|
)
|
||||||
|
.select_related('type')
|
||||||
|
.select_related('machine__switch__stack'))
|
||||||
return nas.first()
|
return nas.first()
|
||||||
|
|
||||||
|
|
||||||
def check_user_machine_and_register(nas_type, username, mac_address):
|
def check_user_machine_and_register(nas_type, username, mac_address):
|
||||||
""" Verifie le username et la mac renseignee. L'enregistre si elle est inconnue.
|
"""Verifie le username et la mac renseignee. L'enregistre si elle est
|
||||||
|
inconnue.
|
||||||
Renvoie le mot de passe ntlm de l'user si tout est ok
|
Renvoie le mot de passe ntlm de l'user si tout est ok
|
||||||
Utilise pour les authentifications en 802.1X"""
|
Utilise pour les authentifications en 802.1X"""
|
||||||
interface = Interface.objects.filter(mac_address=mac_address).first()
|
interface = Interface.objects.filter(mac_address=mac_address).first()
|
||||||
|
@ -252,7 +289,10 @@ def check_user_machine_and_register(nas_type, username, mac_address):
|
||||||
return (False, u"Adhérent non cotisant", '')
|
return (False, u"Adhérent non cotisant", '')
|
||||||
if interface:
|
if interface:
|
||||||
if interface.machine.user != user:
|
if interface.machine.user != user:
|
||||||
return (False, u"Machine enregistrée sur le compte d'un autre user...", '')
|
return (False,
|
||||||
|
u"Machine enregistrée sur le compte d'un autre "
|
||||||
|
"user...",
|
||||||
|
'')
|
||||||
elif not interface.is_active:
|
elif not interface.is_active:
|
||||||
return (False, u"Machine desactivée", '')
|
return (False, u"Machine desactivée", '')
|
||||||
elif not interface.ipv4:
|
elif not interface.ipv4:
|
||||||
|
@ -264,7 +304,9 @@ def check_user_machine_and_register(nas_type, username, mac_address):
|
||||||
if nas_type.autocapture_mac:
|
if nas_type.autocapture_mac:
|
||||||
result, reason = user.autoregister_machine(mac_address, nas_type)
|
result, reason = user.autoregister_machine(mac_address, nas_type)
|
||||||
if result:
|
if result:
|
||||||
return (True, u'Access Ok, Capture de la mac...', user.pwd_ntlm)
|
return (True,
|
||||||
|
u'Access Ok, Capture de la mac...',
|
||||||
|
user.pwd_ntlm)
|
||||||
else:
|
else:
|
||||||
return (False, u'Erreur dans le register mac %s' % reason, '')
|
return (False, u'Erreur dans le register mac %s' % reason, '')
|
||||||
else:
|
else:
|
||||||
|
@ -273,8 +315,10 @@ def check_user_machine_and_register(nas_type, username, mac_address):
|
||||||
return (False, u"Machine inconnue", '')
|
return (False, u"Machine inconnue", '')
|
||||||
|
|
||||||
|
|
||||||
def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_address):
|
def decide_vlan_and_register_switch(nas_machine, nas_type, port_number,
|
||||||
"""Fonction de placement vlan pour un switch en radius filaire auth par mac.
|
mac_address):
|
||||||
|
"""Fonction de placement vlan pour un switch en radius filaire auth par
|
||||||
|
mac.
|
||||||
Plusieurs modes :
|
Plusieurs modes :
|
||||||
- nas inconnu, port inconnu : on place sur le vlan par defaut VLAN_OK
|
- nas inconnu, port inconnu : on place sur le vlan par defaut VLAN_OK
|
||||||
- pas de radius sur le port : VLAN_OK
|
- pas de radius sur le port : VLAN_OK
|
||||||
|
@ -292,7 +336,8 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_addr
|
||||||
- interface inconnue :
|
- interface inconnue :
|
||||||
- register mac désactivé : VLAN_NOK
|
- register mac désactivé : VLAN_NOK
|
||||||
- register mac activé :
|
- register mac activé :
|
||||||
- dans la chambre associé au port, pas d'user ou non à jour : VLAN_NOK
|
- dans la chambre associé au port, pas d'user ou non à
|
||||||
|
jour : VLAN_NOK
|
||||||
- user à jour, autocapture de la mac et VLAN_OK
|
- user à jour, autocapture de la mac et VLAN_OK
|
||||||
"""
|
"""
|
||||||
# Get port from switch and port number
|
# Get port from switch and port number
|
||||||
|
@ -303,8 +348,13 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_addr
|
||||||
|
|
||||||
sw_name = str(nas_machine)
|
sw_name = str(nas_machine)
|
||||||
|
|
||||||
port = Port.objects.filter(switch=Switch.objects.filter(machine_ptr=nas_machine), port=port_number).first()
|
port = (Port.objects
|
||||||
#Si le port est inconnu, on place sur le vlan defaut
|
.filter(
|
||||||
|
switch=Switch.objects.filter(machine_ptr=nas_machine),
|
||||||
|
port=port_number
|
||||||
|
)
|
||||||
|
.first())
|
||||||
|
# Si le port est inconnu, on place sur le vlan defaut
|
||||||
if not port:
|
if not port:
|
||||||
return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK)
|
return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK)
|
||||||
|
|
||||||
|
@ -316,7 +366,10 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_addr
|
||||||
DECISION_VLAN = VLAN_OK
|
DECISION_VLAN = VLAN_OK
|
||||||
|
|
||||||
if port.radius == 'NO':
|
if port.radius == 'NO':
|
||||||
return (sw_name, "", u"Pas d'authentification sur ce port" + extra_log, DECISION_VLAN)
|
return (sw_name,
|
||||||
|
"",
|
||||||
|
u"Pas d'authentification sur ce port" + extra_log,
|
||||||
|
DECISION_VLAN)
|
||||||
|
|
||||||
if port.radius == 'BLOQ':
|
if port.radius == 'BLOQ':
|
||||||
return (sw_name, port.room, u'Port desactive', VLAN_NOK)
|
return (sw_name, port.room, u'Port desactive', VLAN_NOK)
|
||||||
|
@ -326,7 +379,9 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_addr
|
||||||
if not room:
|
if not room:
|
||||||
return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK)
|
return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK)
|
||||||
|
|
||||||
room_user = User.objects.filter(Q(club__room=port.room) | Q(adherent__room=port.room))
|
room_user = User.objects.filter(
|
||||||
|
Q(club__room=port.room) | Q(adherent__room=port.room)
|
||||||
|
)
|
||||||
if not room_user:
|
if not room_user:
|
||||||
return (sw_name, room, u'Chambre non cotisante', VLAN_NOK)
|
return (sw_name, room, u'Chambre non cotisante', VLAN_NOK)
|
||||||
for user in room_user:
|
for user in room_user:
|
||||||
|
@ -336,35 +391,78 @@ def decide_vlan_and_register_switch(nas_machine, nas_type, port_number, mac_addr
|
||||||
|
|
||||||
if port.radius == 'COMMON' or port.radius == 'STRICT':
|
if port.radius == 'COMMON' or port.radius == 'STRICT':
|
||||||
# Authentification par mac
|
# Authentification par mac
|
||||||
interface = Interface.objects.filter(mac_address=mac_address).select_related('machine__user').select_related('ipv4').first()
|
interface = (Interface.objects
|
||||||
|
.filter(mac_address=mac_address)
|
||||||
|
.select_related('machine__user')
|
||||||
|
.select_related('ipv4')
|
||||||
|
.first())
|
||||||
if not interface:
|
if not interface:
|
||||||
room = port.room
|
room = port.room
|
||||||
# On essaye de register la mac
|
# On essaye de register la mac
|
||||||
if not nas_type.autocapture_mac:
|
if not nas_type.autocapture_mac:
|
||||||
return (sw_name, "", u'Machine inconnue', VLAN_NOK)
|
return (sw_name, "", u'Machine inconnue', VLAN_NOK)
|
||||||
elif not room:
|
elif not room:
|
||||||
return (sw_name, "Inconnue", u'Chambre et machine inconnues', VLAN_NOK)
|
return (sw_name,
|
||||||
|
"Inconnue",
|
||||||
|
u'Chambre et machine inconnues',
|
||||||
|
VLAN_NOK)
|
||||||
else:
|
else:
|
||||||
if not room_user:
|
if not room_user:
|
||||||
room_user = User.objects.filter(Q(club__room=port.room) | Q(adherent__room=port.room))
|
room_user = User.objects.filter(
|
||||||
|
Q(club__room=port.room) | Q(adherent__room=port.room)
|
||||||
|
)
|
||||||
if not room_user:
|
if not room_user:
|
||||||
return (sw_name, room, u'Machine et propriétaire de la chambre inconnus', VLAN_NOK)
|
return (sw_name,
|
||||||
|
room,
|
||||||
|
u'Machine et propriétaire de la chambre '
|
||||||
|
'inconnus',
|
||||||
|
VLAN_NOK)
|
||||||
elif room_user.count() > 1:
|
elif room_user.count() > 1:
|
||||||
return (sw_name, room, u'Machine inconnue, il y a au moins 2 users dans la chambre/local -> ajout de mac automatique impossible', VLAN_NOK)
|
return (sw_name,
|
||||||
|
room,
|
||||||
|
u'Machine inconnue, il y a au moins 2 users '
|
||||||
|
'dans la chambre/local -> ajout de mac '
|
||||||
|
'automatique impossible',
|
||||||
|
VLAN_NOK)
|
||||||
elif not room_user.first().has_access():
|
elif not room_user.first().has_access():
|
||||||
return (sw_name, room, u'Machine inconnue et adhérent non cotisant', VLAN_NOK)
|
return (sw_name,
|
||||||
|
room,
|
||||||
|
u'Machine inconnue et adhérent non cotisant',
|
||||||
|
VLAN_NOK)
|
||||||
else:
|
else:
|
||||||
result, reason = room_user.first().autoregister_machine(mac_address, nas_type)
|
result, reason = (room_user
|
||||||
|
.first()
|
||||||
|
.autoregister_machine(
|
||||||
|
mac_address,
|
||||||
|
nas_type
|
||||||
|
))
|
||||||
if result:
|
if result:
|
||||||
return (sw_name, room, u'Access Ok, Capture de la mac...' + extra_log, DECISION_VLAN)
|
return (sw_name,
|
||||||
|
room,
|
||||||
|
u'Access Ok, Capture de la mac: ' + extra_log,
|
||||||
|
DECISION_VLAN)
|
||||||
else:
|
else:
|
||||||
return (sw_name, room, u'Erreur dans le register mac %s' % reason + unicode(mac_address), VLAN_NOK)
|
return (sw_name,
|
||||||
|
room,
|
||||||
|
u'Erreur dans le register mac %s' % (
|
||||||
|
reason + str(mac_address)
|
||||||
|
),
|
||||||
|
VLAN_NOK)
|
||||||
else:
|
else:
|
||||||
room = port.room
|
room = port.room
|
||||||
if not interface.is_active:
|
if not interface.is_active:
|
||||||
return (sw_name, room, u'Machine non active / adherent non cotisant', VLAN_NOK)
|
return (sw_name,
|
||||||
|
room,
|
||||||
|
u'Machine non active / adherent non cotisant',
|
||||||
|
VLAN_NOK)
|
||||||
elif not interface.ipv4:
|
elif not interface.ipv4:
|
||||||
interface.assign_ipv4()
|
interface.assign_ipv4()
|
||||||
return (sw_name, room, u"Ok, Reassignation de l'ipv4" + extra_log, DECISION_VLAN)
|
return (sw_name,
|
||||||
|
room,
|
||||||
|
u"Ok, Reassignation de l'ipv4" + extra_log,
|
||||||
|
DECISION_VLAN)
|
||||||
else:
|
else:
|
||||||
return (sw_name, room, u'Machine OK' + extra_log, DECISION_VLAN)
|
return (sw_name,
|
||||||
|
room,
|
||||||
|
u'Machine OK' + extra_log,
|
||||||
|
DECISION_VLAN)
|
||||||
|
|
|
@ -20,5 +20,8 @@
|
||||||
# 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.
|
||||||
|
"""logs
|
||||||
|
The app in charge of the stats and logs of what's happening in re2o
|
||||||
|
"""
|
||||||
|
|
||||||
from .acl import *
|
from .acl import *
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
Here are defined some functions to check acl on the application.
|
Here are defined some functions to check acl on the application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def can_view(user):
|
def can_view(user):
|
||||||
"""Check if an user can view the application.
|
"""Check if an user can view the application.
|
||||||
|
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
# -*- 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
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
# Register your models here.
|
|
|
@ -1,28 +0,0 @@
|
||||||
# -*- 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
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
# Create your models here.
|
|
|
@ -20,4 +20,3 @@
|
||||||
# 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.
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,16 @@
|
||||||
# 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.
|
||||||
|
"""logs.templatetags.logs_extra
|
||||||
|
A templatetag to get the class name for a given object
|
||||||
|
"""
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def classname(obj):
|
def classname(obj):
|
||||||
|
""" Returns the object class name """
|
||||||
return obj.__class__.__name__
|
return obj.__class__.__name__
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
# 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.
|
||||||
|
"""logs.tests
|
||||||
|
The tests for the Logs module.
|
||||||
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
# from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
216
logs/views.py
216
logs/views.py
|
@ -46,8 +46,6 @@ from django.db.models import Count, Max
|
||||||
from reversion.models import Revision
|
from reversion.models import Revision
|
||||||
from reversion.models import Version, ContentType
|
from reversion.models import Version, ContentType
|
||||||
|
|
||||||
from time import time
|
|
||||||
|
|
||||||
from users.models import (
|
from users.models import (
|
||||||
User,
|
User,
|
||||||
ServiceUser,
|
ServiceUser,
|
||||||
|
@ -109,15 +107,6 @@ from re2o.acl import (
|
||||||
from re2o.utils import all_active_assigned_interfaces_count
|
from re2o.utils import all_active_assigned_interfaces_count
|
||||||
from re2o.utils import all_active_interfaces_count, SortTable
|
from re2o.utils import all_active_interfaces_count, SortTable
|
||||||
|
|
||||||
STATS_DICT = {
|
|
||||||
0: ["Tout", 36],
|
|
||||||
1: ["1 mois", 1],
|
|
||||||
2: ["2 mois", 2],
|
|
||||||
3: ["6 mois", 6],
|
|
||||||
4: ["1 an", 12],
|
|
||||||
5: ["2 an", 24],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_view_app('logs')
|
@can_view_app('logs')
|
||||||
|
@ -227,66 +216,99 @@ def stats_general(request):
|
||||||
_all_baned = all_baned()
|
_all_baned = all_baned()
|
||||||
_all_whitelisted = all_whitelisted()
|
_all_whitelisted = all_whitelisted()
|
||||||
_all_active_interfaces_count = all_active_interfaces_count()
|
_all_active_interfaces_count = all_active_interfaces_count()
|
||||||
_all_active_assigned_interfaces_count = all_active_assigned_interfaces_count()
|
_all_active_assigned_interfaces_count = \
|
||||||
|
all_active_assigned_interfaces_count()
|
||||||
stats = [
|
stats = [
|
||||||
[["Categorie", "Nombre d'utilisateurs (total club et adhérents)", "Nombre d'adhérents", "Nombre de clubs"], {
|
[ # First set of data (about users)
|
||||||
'active_users': [
|
[ # Headers
|
||||||
"Users actifs",
|
"Categorie",
|
||||||
User.objects.filter(state=User.STATE_ACTIVE).count(),
|
"Nombre d'utilisateurs (total club et adhérents)",
|
||||||
Adherent.objects.filter(state=Adherent.STATE_ACTIVE).count(),
|
"Nombre d'adhérents",
|
||||||
Club.objects.filter(state=Club.STATE_ACTIVE).count()],
|
"Nombre de clubs"
|
||||||
'inactive_users': [
|
],
|
||||||
"Users désactivés",
|
{ # Data
|
||||||
User.objects.filter(state=User.STATE_DISABLED).count(),
|
'active_users': [
|
||||||
Adherent.objects.filter(state=Adherent.STATE_DISABLED).count(),
|
"Users actifs",
|
||||||
Club.objects.filter(state=Club.STATE_DISABLED).count()],
|
User.objects.filter(state=User.STATE_ACTIVE).count(),
|
||||||
'archive_users': [
|
(Adherent.objects
|
||||||
"Users archivés",
|
.filter(state=Adherent.STATE_ACTIVE)
|
||||||
User.objects.filter(state=User.STATE_ARCHIVE).count(),
|
.count()),
|
||||||
Adherent.objects.filter(state=Adherent.STATE_ARCHIVE).count(),
|
Club.objects.filter(state=Club.STATE_ACTIVE).count()
|
||||||
Club.objects.filter(state=Club.STATE_ARCHIVE).count()],
|
],
|
||||||
'adherent_users': [
|
'inactive_users': [
|
||||||
"Cotisant à l'association",
|
"Users désactivés",
|
||||||
_all_adherent.count(),
|
User.objects.filter(state=User.STATE_DISABLED).count(),
|
||||||
_all_adherent.exclude(adherent__isnull=True).count(),
|
(Adherent.objects
|
||||||
_all_adherent.exclude(club__isnull=True).count()],
|
.filter(state=Adherent.STATE_DISABLED)
|
||||||
'connexion_users': [
|
.count()),
|
||||||
"Utilisateurs bénéficiant d'une connexion",
|
Club.objects.filter(state=Club.STATE_DISABLED).count()
|
||||||
_all_has_access.count(),
|
],
|
||||||
_all_has_access.exclude(adherent__isnull=True).count(),
|
'archive_users': [
|
||||||
_all_has_access.exclude(club__isnull=True).count()],
|
"Users archivés",
|
||||||
'ban_users': [
|
User.objects.filter(state=User.STATE_ARCHIVE).count(),
|
||||||
"Utilisateurs bannis",
|
(Adherent.objects
|
||||||
_all_baned.count(),
|
.filter(state=Adherent.STATE_ARCHIVE)
|
||||||
_all_baned.exclude(adherent__isnull=True).count(),
|
.count()),
|
||||||
_all_baned.exclude(club__isnull=True).count()],
|
Club.objects.filter(state=Club.STATE_ARCHIVE).count()
|
||||||
'whitelisted_user': [
|
],
|
||||||
"Utilisateurs bénéficiant d'une connexion gracieuse",
|
'adherent_users': [
|
||||||
_all_whitelisted.count(),
|
"Cotisant à l'association",
|
||||||
_all_whitelisted.exclude(adherent__isnull=True).count(),
|
_all_adherent.count(),
|
||||||
_all_whitelisted.exclude(club__isnull=True).count()],
|
_all_adherent.exclude(adherent__isnull=True).count(),
|
||||||
'actives_interfaces': [
|
_all_adherent.exclude(club__isnull=True).count()
|
||||||
"Interfaces actives (ayant accès au reseau)",
|
],
|
||||||
_all_active_interfaces_count.count(),
|
'connexion_users': [
|
||||||
_all_active_interfaces_count.exclude(
|
"Utilisateurs bénéficiant d'une connexion",
|
||||||
machine__user__adherent__isnull=True
|
_all_has_access.count(),
|
||||||
).count(),
|
_all_has_access.exclude(adherent__isnull=True).count(),
|
||||||
_all_active_interfaces_count.exclude(
|
_all_has_access.exclude(club__isnull=True).count()
|
||||||
machine__user__club__isnull=True
|
],
|
||||||
).count()],
|
'ban_users': [
|
||||||
'actives_assigned_interfaces': [
|
"Utilisateurs bannis",
|
||||||
"Interfaces actives et assignées ipv4",
|
_all_baned.count(),
|
||||||
_all_active_assigned_interfaces_count.count(),
|
_all_baned.exclude(adherent__isnull=True).count(),
|
||||||
_all_active_assigned_interfaces_count.exclude(
|
_all_baned.exclude(club__isnull=True).count()
|
||||||
machine__user__adherent__isnull=True
|
],
|
||||||
).count(),
|
'whitelisted_user': [
|
||||||
_all_active_assigned_interfaces_count.exclude(
|
"Utilisateurs bénéficiant d'une connexion gracieuse",
|
||||||
machine__user__club__isnull=True
|
_all_whitelisted.count(),
|
||||||
).count()]
|
_all_whitelisted.exclude(adherent__isnull=True).count(),
|
||||||
}],
|
_all_whitelisted.exclude(club__isnull=True).count()
|
||||||
[["Range d'ip", "Vlan", "Nombre d'ip totales", "Ip assignées",
|
],
|
||||||
"Ip assignées à une machine active", "Ip non assignées"], ip_dict]
|
'actives_interfaces': [
|
||||||
|
"Interfaces actives (ayant accès au reseau)",
|
||||||
|
_all_active_interfaces_count.count(),
|
||||||
|
(_all_active_interfaces_count
|
||||||
|
.exclude(machine__user__adherent__isnull=True)
|
||||||
|
.count()),
|
||||||
|
(_all_active_interfaces_count
|
||||||
|
.exclude(machine__user__club__isnull=True)
|
||||||
|
.count())
|
||||||
|
],
|
||||||
|
'actives_assigned_interfaces': [
|
||||||
|
"Interfaces actives et assignées ipv4",
|
||||||
|
_all_active_assigned_interfaces_count.count(),
|
||||||
|
(_all_active_assigned_interfaces_count
|
||||||
|
.exclude(machine__user__adherent__isnull=True)
|
||||||
|
.count()),
|
||||||
|
(_all_active_assigned_interfaces_count
|
||||||
|
.exclude(machine__user__club__isnull=True)
|
||||||
|
.count())
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[ # Second set of data (about ip adresses)
|
||||||
|
[ # Headers
|
||||||
|
"Range d'ip",
|
||||||
|
"Vlan",
|
||||||
|
"Nombre d'ip totales",
|
||||||
|
"Ip assignées",
|
||||||
|
"Ip assignées à une machine active",
|
||||||
|
"Ip non assignées"
|
||||||
|
],
|
||||||
|
ip_dict # Data already prepared
|
||||||
]
|
]
|
||||||
|
]
|
||||||
return render(request, 'logs/stats_general.html', {'stats_list': stats})
|
return render(request, 'logs/stats_general.html', {'stats_list': stats})
|
||||||
|
|
||||||
|
|
||||||
|
@ -313,11 +335,26 @@ def stats_models(request):
|
||||||
'whitelist': [Whitelist.PRETTY_NAME, Whitelist.objects.count()]
|
'whitelist': [Whitelist.PRETTY_NAME, Whitelist.objects.count()]
|
||||||
},
|
},
|
||||||
'Cotisations': {
|
'Cotisations': {
|
||||||
'factures': [Facture._meta.verbose_name.title(), Facture.objects.count()],
|
'factures': [
|
||||||
'vente': [Vente._meta.verbose_name.title(), Vente.objects.count()],
|
Facture._meta.verbose_name.title(),
|
||||||
'cotisation': [Cotisation._meta.verbose_name.title(), Cotisation.objects.count()],
|
Facture.objects.count()
|
||||||
'article': [Article._meta.verbose_name.title(), Article.objects.count()],
|
],
|
||||||
'banque': [Banque._meta.verbose_name.title(), Banque.objects.count()],
|
'vente': [
|
||||||
|
Vente._meta.verbose_name.title(),
|
||||||
|
Vente.objects.count()
|
||||||
|
],
|
||||||
|
'cotisation': [
|
||||||
|
Cotisation._meta.verbose_name.title(),
|
||||||
|
Cotisation.objects.count()
|
||||||
|
],
|
||||||
|
'article': [
|
||||||
|
Article._meta.verbose_name.title(),
|
||||||
|
Article.objects.count()
|
||||||
|
],
|
||||||
|
'banque': [
|
||||||
|
Banque._meta.verbose_name.title(),
|
||||||
|
Banque.objects.count()
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'Machines': {
|
'Machines': {
|
||||||
'machine': [Machine.PRETTY_NAME, Machine.objects.count()],
|
'machine': [Machine.PRETTY_NAME, Machine.objects.count()],
|
||||||
|
@ -370,12 +407,6 @@ def stats_users(request):
|
||||||
nombre de machines par user, d'etablissements par user,
|
nombre de machines par user, d'etablissements par user,
|
||||||
de moyens de paiements par user, de banque par user,
|
de moyens de paiements par user, de banque par user,
|
||||||
de bannissement par user, etc"""
|
de bannissement par user, etc"""
|
||||||
onglet = request.GET.get('onglet')
|
|
||||||
try:
|
|
||||||
_search_field = STATS_DICT[onglet]
|
|
||||||
except KeyError:
|
|
||||||
_search_field = STATS_DICT[0]
|
|
||||||
onglet = 0
|
|
||||||
stats = {
|
stats = {
|
||||||
'Utilisateur': {
|
'Utilisateur': {
|
||||||
'Machines': User.objects.annotate(
|
'Machines': User.objects.annotate(
|
||||||
|
@ -410,11 +441,7 @@ def stats_users(request):
|
||||||
).order_by('-num')[:10],
|
).order_by('-num')[:10],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return render(request, 'logs/stats_users.html', {
|
return render(request, 'logs/stats_users.html', {'stats_list': stats})
|
||||||
'stats_list': stats,
|
|
||||||
'stats_dict': STATS_DICT,
|
|
||||||
'active_field': onglet
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -432,14 +459,21 @@ def stats_actions(request):
|
||||||
}
|
}
|
||||||
return render(request, 'logs/stats_users.html', {'stats_list': stats})
|
return render(request, 'logs/stats_users.html', {'stats_list': stats})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_view_app('users')
|
@can_view_app('users')
|
||||||
def stats_droits(request):
|
def stats_droits(request):
|
||||||
"""Affiche la liste des droits et les users ayant chaque droit"""
|
"""Affiche la liste des droits et les users ayant chaque droit"""
|
||||||
depart=time()
|
stats_list = {}
|
||||||
stats_list={}
|
|
||||||
|
|
||||||
for droit in ListRight.objects.all().select_related('group_ptr'):
|
|
||||||
stats_list[droit]=droit.user_set.all().annotate(num=Count('revision'),last=Max('revision__date_created'))
|
|
||||||
|
|
||||||
return render(request, 'logs/stats_droits.html', {'stats_list': stats_list})
|
for droit in ListRight.objects.all().select_related('group_ptr'):
|
||||||
|
stats_list[droit] = droit.user_set.all().annotate(
|
||||||
|
num=Count('revision'),
|
||||||
|
last=Max('revision__date_created')
|
||||||
|
)
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'logs/stats_droits.html',
|
||||||
|
{'stats_list': stats_list}
|
||||||
|
)
|
||||||
|
|
|
@ -20,5 +20,8 @@
|
||||||
# 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.
|
||||||
|
"""machines
|
||||||
|
The app in charge of everything related to the machines, the interface, ...
|
||||||
|
"""
|
||||||
|
|
||||||
from .acl import *
|
from .acl import *
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
Here are defined some functions to check acl on the application.
|
Here are defined some functions to check acl on the application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def can_view(user):
|
def can_view(user):
|
||||||
"""Check if an user can view the application.
|
"""Check if an user can view the application.
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
# 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.
|
||||||
|
"""machines.admin
|
||||||
|
The objects, fields and datastructures visible in the Django admin view
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
@ -44,74 +47,92 @@ from .models import (
|
||||||
|
|
||||||
|
|
||||||
class MachineAdmin(VersionAdmin):
|
class MachineAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a Machine object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Ipv6ListAdmin(VersionAdmin):
|
class Ipv6ListAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a Ipv6List object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class IpTypeAdmin(VersionAdmin):
|
class IpTypeAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a IpType object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MachineTypeAdmin(VersionAdmin):
|
class MachineTypeAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a MachineType object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class VlanAdmin(VersionAdmin):
|
class VlanAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a Vlan object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ExtensionAdmin(VersionAdmin):
|
class ExtensionAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a Extension object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SOAAdmin(VersionAdmin):
|
class SOAAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a SOA object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MxAdmin(VersionAdmin):
|
class MxAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a MX object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NsAdmin(VersionAdmin):
|
class NsAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a NS object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TxtAdmin(VersionAdmin):
|
class TxtAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a TXT object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SrvAdmin(VersionAdmin):
|
class SrvAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a SRV object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NasAdmin(VersionAdmin):
|
class NasAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a Nas object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class IpListAdmin(VersionAdmin):
|
class IpListAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a Ipv4List object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OuverturePortAdmin(VersionAdmin):
|
class OuverturePortAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a OuverturePort object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OuverturePortListAdmin(VersionAdmin):
|
class OuverturePortListAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a OuverturePortList object """
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class InterfaceAdmin(VersionAdmin):
|
class InterfaceAdmin(VersionAdmin):
|
||||||
list_display = ('machine','type','mac_address','ipv4','details')
|
""" Admin view of a Interface object """
|
||||||
|
list_display = ('machine', 'type', 'mac_address', 'ipv4', 'details')
|
||||||
|
|
||||||
|
|
||||||
class DomainAdmin(VersionAdmin):
|
class DomainAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a Domain object """
|
||||||
list_display = ('interface_parent', 'name', 'extension', 'cname')
|
list_display = ('interface_parent', 'name', 'extension', 'cname')
|
||||||
|
|
||||||
|
|
||||||
class ServiceAdmin(VersionAdmin):
|
class ServiceAdmin(VersionAdmin):
|
||||||
|
""" Admin view of a ServiceAdmin object """
|
||||||
list_display = ('service_type', 'min_time_regen', 'regular_time_regen')
|
list_display = ('service_type', 'min_time_regen', 'regular_time_regen')
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,5 +154,3 @@ admin.site.register(Ipv6List, Ipv6ListAdmin)
|
||||||
admin.site.register(Nas, NasAdmin)
|
admin.site.register(Nas, NasAdmin)
|
||||||
admin.site.register(OuverturePort, OuverturePortAdmin)
|
admin.site.register(OuverturePort, OuverturePortAdmin)
|
||||||
admin.site.register(OuverturePortList, OuverturePortListAdmin)
|
admin.site.register(OuverturePortList, OuverturePortListAdmin)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,8 @@ class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
|
||||||
self.fields['type'].label = 'Type de machine'
|
self.fields['type'].label = 'Type de machine'
|
||||||
self.fields['type'].empty_label = "Séléctionner un type de machine"
|
self.fields['type'].empty_label = "Séléctionner un type de machine"
|
||||||
if "ipv4" in self.fields:
|
if "ipv4" in self.fields:
|
||||||
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
|
self.fields['ipv4'].empty_label = ("Assignation automatique de "
|
||||||
|
"l'ipv4")
|
||||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||||
interface__isnull=True
|
interface__isnull=True
|
||||||
)
|
)
|
||||||
|
@ -136,10 +137,10 @@ class AliasForm(FormRevMixin, ModelForm):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
user = kwargs.pop('user')
|
user = kwargs.pop('user')
|
||||||
super(AliasForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(AliasForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
can_use_all, reason = Extension.can_use_all(user)
|
can_use_all, _reason = Extension.can_use_all(user)
|
||||||
if not can_use_all:
|
if not can_use_all:
|
||||||
self.fields['extension'].queryset = Extension.objects.filter(
|
self.fields['extension'].queryset = Extension.objects.filter(
|
||||||
need_infra=False
|
need_infra=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -328,6 +329,7 @@ class MxForm(FormRevMixin, ModelForm):
|
||||||
interface_parent=None
|
interface_parent=None
|
||||||
).select_related('extension')
|
).select_related('extension')
|
||||||
|
|
||||||
|
|
||||||
class DelMxForm(FormRevMixin, Form):
|
class DelMxForm(FormRevMixin, Form):
|
||||||
"""Suppression d'un ou plusieurs MX"""
|
"""Suppression d'un ou plusieurs MX"""
|
||||||
mx = forms.ModelMultipleChoiceField(
|
mx = forms.ModelMultipleChoiceField(
|
||||||
|
@ -472,10 +474,14 @@ class ServiceForm(FormRevMixin, ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
self.fields['servers'].queryset = Interface.objects.all()\
|
self.fields['servers'].queryset = (Interface.objects.all()
|
||||||
.select_related('domain__extension')
|
.select_related(
|
||||||
|
'domain__extension'
|
||||||
|
))
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
|
# TODO : None of the parents of ServiceForm use the commit
|
||||||
|
# parameter in .save()
|
||||||
instance = super(ServiceForm, self).save(commit=False)
|
instance = super(ServiceForm, self).save(commit=False)
|
||||||
if commit:
|
if commit:
|
||||||
instance.save()
|
instance.save()
|
||||||
|
|
21
machines/migrations/0078_auto_20180415_1252.py
Normal file
21
machines/migrations/0078_auto_20180415_1252.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-04-15 10:52
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0077_auto_20180409_2243'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='srv',
|
||||||
|
name='priority',
|
||||||
|
field=models.PositiveIntegerField(default=0, help_text="La priorité du serveur cible (valeur entière non négative, plus elle est faible, plus ce serveur sera utilisé s'il est disponible)", validators=[django.core.validators.MaxValueValidator(65535)]),
|
||||||
|
),
|
||||||
|
]
|
|
@ -20,14 +20,17 @@
|
||||||
# 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.
|
||||||
|
"""machines.models
|
||||||
|
The models definitions for the Machines app
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import re
|
import re
|
||||||
from netaddr import mac_bare, EUI, IPSet, IPRange, IPNetwork, IPAddress
|
|
||||||
from ipaddress import IPv6Address
|
from ipaddress import IPv6Address
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
from netaddr import mac_bare, EUI, IPSet, IPRange, IPNetwork, IPAddress
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import post_save, post_delete
|
from django.db.models.signals import post_save, post_delete
|
||||||
|
@ -40,7 +43,7 @@ from django.core.validators import MaxValueValidator
|
||||||
from macaddress.fields import MACAddressField
|
from macaddress.fields import MACAddressField
|
||||||
|
|
||||||
from re2o.field_permissions import FieldPermissionModelMixin
|
from re2o.field_permissions import FieldPermissionModelMixin
|
||||||
from re2o.mixins import AclMixin, RevMixin
|
from re2o.mixins import AclMixin, RevMixin
|
||||||
|
|
||||||
import users.models
|
import users.models
|
||||||
import preferences.models
|
import preferences.models
|
||||||
|
@ -63,23 +66,30 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
("view_machine", "Peut voir un objet machine quelquonque"),
|
("view_machine", "Peut voir un objet machine quelquonque"),
|
||||||
("change_machine_user", "Peut changer le propriétaire d'une machine"),
|
("change_machine_user",
|
||||||
|
"Peut changer le propriétaire d'une machine"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_instance(machineid, *args, **kwargs):
|
@classmethod
|
||||||
|
def get_instance(cls, machineid, *_args, **_kwargs):
|
||||||
"""Get the Machine instance with machineid.
|
"""Get the Machine instance with machineid.
|
||||||
:param userid: The id
|
:param userid: The id
|
||||||
:return: The user
|
:return: The user
|
||||||
"""
|
"""
|
||||||
return Machine.objects.get(pk=machineid)
|
return cls.objects.get(pk=machineid)
|
||||||
|
|
||||||
def linked_objects(self):
|
def linked_objects(self):
|
||||||
"""Return linked objects : machine and domain.
|
"""Return linked objects : machine and domain.
|
||||||
Usefull in history display"""
|
Usefull in history display"""
|
||||||
return chain(self.interface_set.all(), Domain.objects.filter(interface_parent__in=self.interface_set.all()))
|
return chain(
|
||||||
|
self.interface_set.all(),
|
||||||
|
Domain.objects.filter(
|
||||||
|
interface_parent__in=self.interface_set.all()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_change_user(user_request, *args, **kwargs):
|
def can_change_user(user_request, *_args, **_kwargs):
|
||||||
"""Checks if an user is allowed to change the user who owns a
|
"""Checks if an user is allowed to change the user who owns a
|
||||||
Machine.
|
Machine.
|
||||||
|
|
||||||
|
@ -90,18 +100,22 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
|
||||||
A tuple with a boolean stating if edition is allowed and an
|
A tuple with a boolean stating if edition is allowed and an
|
||||||
explanation message.
|
explanation message.
|
||||||
"""
|
"""
|
||||||
return user_request.has_perm('machines.change_machine_user'), "Vous ne pouvez pas modifier l'utilisateur de la machine."
|
return (user_request.has_perm('machines.change_machine_user'),
|
||||||
|
"Vous ne pouvez pas modifier l'utilisateur de la machine.")
|
||||||
|
|
||||||
def can_view_all(user_request, *args, **kwargs):
|
@staticmethod
|
||||||
|
def can_view_all(user_request, *_args, **_kwargs):
|
||||||
"""Vérifie qu'on peut bien afficher l'ensemble des machines,
|
"""Vérifie qu'on peut bien afficher l'ensemble des machines,
|
||||||
droit particulier correspondant
|
droit particulier correspondant
|
||||||
:param user_request: instance user qui fait l'edition
|
:param user_request: instance user qui fait l'edition
|
||||||
:return: True ou False avec la raison de l'échec le cas échéant"""
|
:return: True ou False avec la raison de l'échec le cas échéant"""
|
||||||
if not user_request.has_perm('machines.view_machine'):
|
if not user_request.has_perm('machines.view_machine'):
|
||||||
return False, u"Vous ne pouvez pas afficher l'ensemble des machines sans permission"
|
return False, (u"Vous ne pouvez pas afficher l'ensemble des "
|
||||||
|
"machines sans permission")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def can_create(user_request, userid, *args, **kwargs):
|
@staticmethod
|
||||||
|
def can_create(user_request, userid, *_args, **_kwargs):
|
||||||
"""Vérifie qu'un user qui fait la requète peut bien créer la machine
|
"""Vérifie qu'un user qui fait la requète peut bien créer la machine
|
||||||
et n'a pas atteint son quota, et crée bien une machine à lui
|
et n'a pas atteint son quota, et crée bien une machine à lui
|
||||||
:param user_request: Utilisateur qui fait la requête
|
:param user_request: Utilisateur qui fait la requête
|
||||||
|
@ -111,17 +125,21 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
|
||||||
user = users.models.User.objects.get(pk=userid)
|
user = users.models.User.objects.get(pk=userid)
|
||||||
except users.models.User.DoesNotExist:
|
except users.models.User.DoesNotExist:
|
||||||
return False, u"Utilisateur inexistant"
|
return False, u"Utilisateur inexistant"
|
||||||
max_lambdauser_interfaces = preferences.models.OptionalMachine.get_cached_value('max_lambdauser_interfaces')
|
max_lambdauser_interfaces = (preferences.models.OptionalMachine
|
||||||
|
.get_cached_value(
|
||||||
|
'max_lambdauser_interfaces'
|
||||||
|
))
|
||||||
if not user_request.has_perm('machines.add_machine'):
|
if not user_request.has_perm('machines.add_machine'):
|
||||||
if not preferences.models.OptionalMachine.get_cached_value('create_machine'):
|
if not (preferences.models.OptionalMachine
|
||||||
|
.get_cached_value('create_machine')):
|
||||||
return False, u"Vous ne pouvez pas ajouter une machine"
|
return False, u"Vous ne pouvez pas ajouter une machine"
|
||||||
if user != user_request:
|
if user != user_request:
|
||||||
return False, u"Vous ne pouvez pas ajouter une machine à un\
|
return False, (u"Vous ne pouvez pas ajouter une machine à un "
|
||||||
autre user que vous sans droit"
|
"autre user que vous sans droit")
|
||||||
if user.user_interfaces().count() >= max_lambdauser_interfaces:
|
if user.user_interfaces().count() >= max_lambdauser_interfaces:
|
||||||
return False, u"Vous avez atteint le maximum d'interfaces\
|
return False, (u"Vous avez atteint le maximum d'interfaces "
|
||||||
autorisées que vous pouvez créer vous même (%s) "\
|
"autorisées que vous pouvez créer vous même "
|
||||||
% max_lambdauser_interfaces
|
"(%s) " % max_lambdauser_interfaces)
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def can_edit(self, user_request, *args, **kwargs):
|
def can_edit(self, user_request, *args, **kwargs):
|
||||||
|
@ -131,9 +149,15 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
|
||||||
:param user_request: instance user qui fait l'edition
|
:param user_request: instance user qui fait l'edition
|
||||||
:return: True ou False avec la raison le cas échéant"""
|
:return: True ou False avec la raison le cas échéant"""
|
||||||
if self.user != user_request:
|
if self.user != user_request:
|
||||||
if not user_request.has_perm('machines.change_interface') or not self.user.can_edit(self.user, user_request, *args, **kwargs)[0]:
|
if (not user_request.has_perm('machines.change_interface') or
|
||||||
return False, u"Vous ne pouvez pas éditer une machine\
|
not self.user.can_edit(
|
||||||
d'un autre user que vous sans droit"
|
self.user,
|
||||||
|
user_request,
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
)[0]):
|
||||||
|
return False, (u"Vous ne pouvez pas éditer une machine "
|
||||||
|
"d'un autre user que vous sans droit")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def can_delete(self, user_request, *args, **kwargs):
|
def can_delete(self, user_request, *args, **kwargs):
|
||||||
|
@ -143,26 +167,33 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model):
|
||||||
:param user_request: instance user qui fait l'edition
|
:param user_request: instance user qui fait l'edition
|
||||||
:return: True ou False avec la raison de l'échec le cas échéant"""
|
:return: True ou False avec la raison de l'échec le cas échéant"""
|
||||||
if self.user != user_request:
|
if self.user != user_request:
|
||||||
if not user_request.has_perm('machines.change_interface') or not self.user.can_edit(self.user, user_request, *args, **kwargs)[0]:
|
if (not user_request.has_perm('machines.change_interface') or
|
||||||
return False, u"Vous ne pouvez pas éditer une machine\
|
not self.user.can_edit(
|
||||||
d'un autre user que vous sans droit"
|
self.user,
|
||||||
|
user_request,
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
)[0]):
|
||||||
|
return False, (u"Vous ne pouvez pas éditer une machine "
|
||||||
|
"d'un autre user que vous sans droit")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
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 (soit
|
"""Vérifie qu'on peut bien voir cette instance particulière (soit
|
||||||
machine de soi, soit droit particulier
|
machine de soi, soit droit particulier
|
||||||
:param self: instance machine à éditer
|
:param self: instance machine à éditer
|
||||||
:param user_request: instance user qui fait l'edition
|
:param user_request: instance user qui fait l'edition
|
||||||
:return: True ou False avec la raison de l'échec le cas échéant"""
|
:return: True ou False avec la raison de l'échec le cas échéant"""
|
||||||
if not user_request.has_perm('machines.view_machine') and self.user != user_request:
|
if (not user_request.has_perm('machines.view_machine') and
|
||||||
return False, u"Vous n'avez pas droit de voir les machines autre\
|
self.user != user_request):
|
||||||
que les vôtres"
|
return False, (u"Vous n'avez pas droit de voir les machines autre "
|
||||||
|
"que les vôtres")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Machine, self).__init__(*args, **kwargs)
|
super(Machine, self).__init__(*args, **kwargs)
|
||||||
self.field_permissions = {
|
self.field_permissions = {
|
||||||
'user' : self.can_change_user,
|
'user': self.can_change_user,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -184,7 +215,8 @@ class MachineType(RevMixin, AclMixin, models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
("view_machinetype", "Peut voir un objet machinetype"),
|
("view_machinetype", "Peut voir un objet machinetype"),
|
||||||
("use_all_machinetype", "Peut utiliser n'importe quel type de machine"),
|
("use_all_machinetype",
|
||||||
|
"Peut utiliser n'importe quel type de machine"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def all_interfaces(self):
|
def all_interfaces(self):
|
||||||
|
@ -192,7 +224,8 @@ class MachineType(RevMixin, AclMixin, models.Model):
|
||||||
machinetype"""
|
machinetype"""
|
||||||
return Interface.objects.filter(type=self)
|
return Interface.objects.filter(type=self)
|
||||||
|
|
||||||
def can_use_all(user_request, *args, **kwargs):
|
@staticmethod
|
||||||
|
def can_use_all(user_request, *_args, **_kwargs):
|
||||||
"""Check if an user can use every MachineType.
|
"""Check if an user can use every MachineType.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -202,7 +235,8 @@ class MachineType(RevMixin, AclMixin, models.Model):
|
||||||
message is acces is not allowed.
|
message is acces is not allowed.
|
||||||
"""
|
"""
|
||||||
if not user_request.has_perm('machines.use_all_machinetype'):
|
if not user_request.has_perm('machines.use_all_machinetype'):
|
||||||
return False, u"Vous n'avez pas le droit d'utiliser tout types de machines"
|
return False, (u"Vous n'avez pas le droit d'utiliser tout types "
|
||||||
|
"de machines")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -300,7 +334,11 @@ class IpType(RevMixin, AclMixin, models.Model):
|
||||||
if not self.prefix_v6:
|
if not self.prefix_v6:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
for ipv6 in Ipv6List.objects.filter(interface__in=Interface.objects.filter(type__in=MachineType.objects.filter(ip_type=self))):
|
for ipv6 in Ipv6List.objects.filter(
|
||||||
|
interface__in=Interface.objects.filter(
|
||||||
|
type__in=MachineType.objects.filter(ip_type=self)
|
||||||
|
)
|
||||||
|
):
|
||||||
ipv6.check_and_replace_prefix(prefix=self.prefix_v6)
|
ipv6.check_and_replace_prefix(prefix=self.prefix_v6)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
@ -329,8 +367,10 @@ class IpType(RevMixin, AclMixin, models.Model):
|
||||||
self.clean()
|
self.clean()
|
||||||
super(IpType, self).save(*args, **kwargs)
|
super(IpType, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def can_use_all(user_request, *args, **kwargs):
|
@staticmethod
|
||||||
"""Superdroit qui permet d'utiliser toutes les extensions sans restrictions
|
def can_use_all(user_request, *_args, **_kwargs):
|
||||||
|
"""Superdroit qui permet d'utiliser toutes les extensions sans
|
||||||
|
restrictions
|
||||||
:param user_request: instance user qui fait l'edition
|
:param user_request: instance user qui fait l'edition
|
||||||
:return: True ou False avec la raison de l'échec le cas échéant"""
|
:return: True ou False avec la raison de l'échec le cas échéant"""
|
||||||
return user_request.has_perm('machines.use_all_iptype'), None
|
return user_request.has_perm('machines.use_all_iptype'), None
|
||||||
|
@ -409,17 +449,17 @@ class SOA(RevMixin, AclMixin, models.Model):
|
||||||
help_text='Email du contact pour la zone'
|
help_text='Email du contact pour la zone'
|
||||||
)
|
)
|
||||||
refresh = models.PositiveIntegerField(
|
refresh = models.PositiveIntegerField(
|
||||||
default=86400, # 24 hours
|
default=86400, # 24 hours
|
||||||
help_text='Secondes avant que les DNS secondaires doivent demander le\
|
help_text='Secondes avant que les DNS secondaires doivent demander le\
|
||||||
serial du DNS primaire pour détecter une modification'
|
serial du DNS primaire pour détecter une modification'
|
||||||
)
|
)
|
||||||
retry = models.PositiveIntegerField(
|
retry = models.PositiveIntegerField(
|
||||||
default=7200, # 2 hours
|
default=7200, # 2 hours
|
||||||
help_text='Secondes avant que les DNS secondaires fassent une nouvelle\
|
help_text='Secondes avant que les DNS secondaires fassent une nouvelle\
|
||||||
demande de serial en cas de timeout du DNS primaire'
|
demande de serial en cas de timeout du DNS primaire'
|
||||||
)
|
)
|
||||||
expire = models.PositiveIntegerField(
|
expire = models.PositiveIntegerField(
|
||||||
default=3600000, # 1000 hours
|
default=3600000, # 1000 hours
|
||||||
help_text='Secondes après lesquelles les DNS secondaires arrêtent de\
|
help_text='Secondes après lesquelles les DNS secondaires arrêtent de\
|
||||||
de répondre aux requêtes en cas de timeout du DNS primaire'
|
de répondre aux requêtes en cas de timeout du DNS primaire'
|
||||||
)
|
)
|
||||||
|
@ -469,8 +509,10 @@ class SOA(RevMixin, AclMixin, models.Model):
|
||||||
extensions .
|
extensions .
|
||||||
/!\ Ne jamais supprimer ou renommer cette fonction car elle est
|
/!\ Ne jamais supprimer ou renommer cette fonction car elle est
|
||||||
utilisée dans les migrations de la BDD. """
|
utilisée dans les migrations de la BDD. """
|
||||||
return cls.objects.get_or_create(name="SOA to edit", mail="postmaser@example.com")[0].pk
|
return cls.objects.get_or_create(
|
||||||
|
name="SOA to edit",
|
||||||
|
mail="postmaser@example.com"
|
||||||
|
)[0].pk
|
||||||
|
|
||||||
|
|
||||||
class Extension(RevMixin, AclMixin, models.Model):
|
class Extension(RevMixin, AclMixin, models.Model):
|
||||||
|
@ -521,8 +563,10 @@ class Extension(RevMixin, AclMixin, models.Model):
|
||||||
entry += "@ IN AAAA " + str(self.origin_v6)
|
entry += "@ IN AAAA " + str(self.origin_v6)
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
def can_use_all(user_request, *args, **kwargs):
|
@staticmethod
|
||||||
"""Superdroit qui permet d'utiliser toutes les extensions sans restrictions
|
def can_use_all(user_request, *_args, **_kwargs):
|
||||||
|
"""Superdroit qui permet d'utiliser toutes les extensions sans
|
||||||
|
restrictions
|
||||||
:param user_request: instance user qui fait l'edition
|
:param user_request: instance user qui fait l'edition
|
||||||
:return: True ou False avec la raison de l'échec le cas échéant"""
|
:return: True ou False avec la raison de l'échec le cas échéant"""
|
||||||
return user_request.has_perm('machines.use_all_extension'), None
|
return user_request.has_perm('machines.use_all_extension'), None
|
||||||
|
@ -555,7 +599,10 @@ class Mx(RevMixin, AclMixin, models.Model):
|
||||||
def dns_entry(self):
|
def dns_entry(self):
|
||||||
"""Renvoie l'entrée DNS complète pour un MX à mettre dans les
|
"""Renvoie l'entrée DNS complète pour un MX à mettre dans les
|
||||||
fichiers de zones"""
|
fichiers de zones"""
|
||||||
return "@ IN MX " + str(self.priority).ljust(3) + " " + str(self.name)
|
return "@ IN MX {prior} {name}".format(
|
||||||
|
prior=str(self.priority).ljust(3),
|
||||||
|
name=str(self.name)
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.zone) + ' ' + str(self.priority) + ' ' + str(self.name)
|
return str(self.zone) + ' ' + str(self.priority) + ' ' + str(self.name)
|
||||||
|
@ -606,12 +653,13 @@ class Txt(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Srv(RevMixin, AclMixin, models.Model):
|
class Srv(RevMixin, AclMixin, models.Model):
|
||||||
|
""" A SRV record """
|
||||||
PRETTY_NAME = "Enregistrement Srv"
|
PRETTY_NAME = "Enregistrement Srv"
|
||||||
|
|
||||||
TCP = 'TCP'
|
TCP = 'TCP'
|
||||||
UDP = 'UDP'
|
UDP = 'UDP'
|
||||||
|
|
||||||
service = models.CharField(max_length=31)
|
service = models.CharField(max_length=31)
|
||||||
protocole = models.CharField(
|
protocole = models.CharField(
|
||||||
max_length=3,
|
max_length=3,
|
||||||
choices=(
|
choices=(
|
||||||
|
@ -628,9 +676,9 @@ class Srv(RevMixin, AclMixin, models.Model):
|
||||||
priority = models.PositiveIntegerField(
|
priority = models.PositiveIntegerField(
|
||||||
default=0,
|
default=0,
|
||||||
validators=[MaxValueValidator(65535)],
|
validators=[MaxValueValidator(65535)],
|
||||||
help_text="La priorité du serveur cible (valeur entière non négative,\
|
help_text=("La priorité du serveur cible (valeur entière non "
|
||||||
plus elle est faible, plus ce serveur sera utilisé s'il est disponible)"
|
"négative, plus elle est faible, plus ce serveur sera "
|
||||||
|
"utilisé s'il est disponible)")
|
||||||
)
|
)
|
||||||
weight = models.PositiveIntegerField(
|
weight = models.PositiveIntegerField(
|
||||||
default=0,
|
default=0,
|
||||||
|
@ -667,7 +715,7 @@ class Srv(RevMixin, AclMixin, models.Model):
|
||||||
str(self.port) + ' ' + str(self.target) + '.'
|
str(self.port) + ' ' + str(self.target) + '.'
|
||||||
|
|
||||||
|
|
||||||
class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model):
|
class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
""" Une interface. Objet clef de l'application machine :
|
""" Une interface. Objet clef de l'application machine :
|
||||||
- une address mac unique. Possibilité de la rendre unique avec le
|
- une address mac unique. Possibilité de la rendre unique avec le
|
||||||
typemachine
|
typemachine
|
||||||
|
@ -692,7 +740,8 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
("view_interface", "Peut voir un objet interface"),
|
("view_interface", "Peut voir un objet interface"),
|
||||||
("change_interface_machine", "Peut changer le propriétaire d'une interface"),
|
("change_interface_machine",
|
||||||
|
"Peut changer le propriétaire d'une interface"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -719,7 +768,10 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model):
|
||||||
prefix_v6 = self.type.ip_type.prefix_v6
|
prefix_v6 = self.type.ip_type.prefix_v6
|
||||||
if not prefix_v6:
|
if not prefix_v6:
|
||||||
return None
|
return None
|
||||||
return IPv6Address(IPv6Address(prefix_v6).exploded[:20] + IPv6Address(self.id).exploded[20:])
|
return IPv6Address(
|
||||||
|
IPv6Address(prefix_v6).exploded[:20] +
|
||||||
|
IPv6Address(self.id).exploded[20:]
|
||||||
|
)
|
||||||
|
|
||||||
def sync_ipv6_dhcpv6(self):
|
def sync_ipv6_dhcpv6(self):
|
||||||
"""Affecte une ipv6 dhcpv6 calculée à partir de l'id de la machine"""
|
"""Affecte une ipv6 dhcpv6 calculée à partir de l'id de la machine"""
|
||||||
|
@ -741,7 +793,9 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model):
|
||||||
ipv6_slaac = self.ipv6_slaac
|
ipv6_slaac = self.ipv6_slaac
|
||||||
if not ipv6_slaac:
|
if not ipv6_slaac:
|
||||||
return
|
return
|
||||||
ipv6_object = Ipv6List.objects.filter(interface=self, slaac_ip=True).first()
|
ipv6_object = (Ipv6List.objects
|
||||||
|
.filter(interface=self, slaac_ip=True)
|
||||||
|
.first())
|
||||||
if not ipv6_object:
|
if not ipv6_object:
|
||||||
ipv6_object = Ipv6List(interface=self, slaac_ip=True)
|
ipv6_object = Ipv6List(interface=self, slaac_ip=True)
|
||||||
if ipv6_object.ipv6 != str(ipv6_slaac):
|
if ipv6_object.ipv6 != str(ipv6_slaac):
|
||||||
|
@ -750,19 +804,24 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model):
|
||||||
|
|
||||||
def sync_ipv6(self):
|
def sync_ipv6(self):
|
||||||
"""Cree et met à jour l'ensemble des ipv6 en fonction du mode choisi"""
|
"""Cree et met à jour l'ensemble des ipv6 en fonction du mode choisi"""
|
||||||
if preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'SLAAC':
|
if (preferences.models.OptionalMachine
|
||||||
|
.get_cached_value('ipv6_mode') == 'SLAAC'):
|
||||||
self.sync_ipv6_slaac()
|
self.sync_ipv6_slaac()
|
||||||
elif preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'DHCPV6':
|
elif (preferences.models.OptionalMachine
|
||||||
|
.get_cached_value('ipv6_mode') == 'DHCPV6'):
|
||||||
self.sync_ipv6_dhcpv6()
|
self.sync_ipv6_dhcpv6()
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
def ipv6(self):
|
def ipv6(self):
|
||||||
""" Renvoie le queryset de la liste des ipv6
|
""" Renvoie le queryset de la liste des ipv6
|
||||||
On renvoie l'ipv6 slaac que si le mode slaac est activé (et non dhcpv6)"""
|
On renvoie l'ipv6 slaac que si le mode slaac est activé
|
||||||
if preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'SLAAC':
|
(et non dhcpv6)"""
|
||||||
|
if (preferences.models.OptionalMachine
|
||||||
|
.get_cached_value('ipv6_mode') == 'SLAAC'):
|
||||||
return self.ipv6list.all()
|
return self.ipv6list.all()
|
||||||
elif preferences.models.OptionalMachine.get_cached_value('ipv6_mode') == 'DHCPV6':
|
elif (preferences.models.OptionalMachine
|
||||||
|
.get_cached_value('ipv6_mode') == 'DHCPV6'):
|
||||||
return self.ipv6list.filter(slaac_ip=False)
|
return self.ipv6list.filter(slaac_ip=False)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
@ -789,7 +848,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model):
|
||||||
# instance.
|
# instance.
|
||||||
# But in our case, it's impossible to create a type value so we raise
|
# But in our case, it's impossible to create a type value so we raise
|
||||||
# the error.
|
# the error.
|
||||||
if not hasattr(self, 'type') :
|
if not hasattr(self, 'type'):
|
||||||
raise ValidationError("Le type d'ip choisi n'est pas valide")
|
raise ValidationError("Le type d'ip choisi n'est pas valide")
|
||||||
self.filter_macaddress()
|
self.filter_macaddress()
|
||||||
self.mac_address = str(EUI(self.mac_address)) or None
|
self.mac_address = str(EUI(self.mac_address)) or None
|
||||||
|
@ -825,7 +884,8 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model):
|
||||||
correspondent pas")
|
correspondent pas")
|
||||||
super(Interface, self).save(*args, **kwargs)
|
super(Interface, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def can_create(user_request, machineid, *args, **kwargs):
|
@staticmethod
|
||||||
|
def can_create(user_request, machineid, *_args, **_kwargs):
|
||||||
"""Verifie que l'user a les bons droits infra pour créer
|
"""Verifie que l'user a les bons droits infra pour créer
|
||||||
une interface, ou bien que la machine appartient bien à l'user
|
une interface, ou bien que la machine appartient bien à l'user
|
||||||
:param macineid: Id de la machine parente de l'interface
|
:param macineid: Id de la machine parente de l'interface
|
||||||
|
@ -836,21 +896,29 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model):
|
||||||
except Machine.DoesNotExist:
|
except Machine.DoesNotExist:
|
||||||
return False, u"Machine inexistante"
|
return False, u"Machine inexistante"
|
||||||
if not user_request.has_perm('machines.add_interface'):
|
if not user_request.has_perm('machines.add_interface'):
|
||||||
if not preferences.models.OptionalMachine.get_cached_value('create_machine'):
|
if not (preferences.models.OptionalMachine
|
||||||
|
.get_cached_value('create_machine')):
|
||||||
return False, u"Vous ne pouvez pas ajouter une machine"
|
return False, u"Vous ne pouvez pas ajouter une machine"
|
||||||
max_lambdauser_interfaces = preferences.models.OptionalMachine.get_cached_value('max_lambdauser_interfaces')
|
max_lambdauser_interfaces = (preferences.models.OptionalMachine
|
||||||
|
.get_cached_value(
|
||||||
|
'max_lambdauser_interfaces'
|
||||||
|
))
|
||||||
if machine.user != user_request:
|
if machine.user != user_request:
|
||||||
return False, u"Vous ne pouvez pas ajouter une interface à une\
|
return False, u"Vous ne pouvez pas ajouter une interface à une\
|
||||||
machine d'un autre user que vous sans droit"
|
machine d'un autre user que vous sans droit"
|
||||||
if machine.user.user_interfaces().count() >= max_lambdauser_interfaces:
|
if (machine.user.user_interfaces().count() >=
|
||||||
|
max_lambdauser_interfaces):
|
||||||
return False, u"Vous avez atteint le maximum d'interfaces\
|
return False, u"Vous avez atteint le maximum d'interfaces\
|
||||||
autorisées que vous pouvez créer vous même (%s) "\
|
autorisées que vous pouvez créer vous même (%s) "\
|
||||||
% max_lambdauser_interfaces
|
% max_lambdauser_interfaces
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_change_machine(user_request, *args, **kwargs):
|
def can_change_machine(user_request, *_args, **_kwargs):
|
||||||
return user_request.has_perm('machines.change_interface_machine'), "Droit requis pour changer la machine"
|
"""Check if a user can change the machine associated with an
|
||||||
|
Interface object """
|
||||||
|
return (user_request.has_perm('machines.change_interface_machine'),
|
||||||
|
"Droit requis pour changer la machine")
|
||||||
|
|
||||||
def can_edit(self, user_request, *args, **kwargs):
|
def can_edit(self, user_request, *args, **kwargs):
|
||||||
"""Verifie que l'user a les bons droits infra pour editer
|
"""Verifie que l'user a les bons droits infra pour editer
|
||||||
|
@ -859,9 +927,14 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model):
|
||||||
:param user_request: Utilisateur qui fait la requête
|
:param user_request: Utilisateur qui fait la requête
|
||||||
:return: soit True, soit False avec la raison de l'échec"""
|
:return: soit True, soit False avec la raison de l'échec"""
|
||||||
if self.machine.user != user_request:
|
if self.machine.user != user_request:
|
||||||
if not user_request.has_perm('machines.change_interface') or not self.machine.user.can_edit(user_request, *args, **kwargs)[0]:
|
if (not user_request.has_perm('machines.change_interface') or
|
||||||
return False, u"Vous ne pouvez pas éditer une machine\
|
not self.machine.user.can_edit(
|
||||||
d'un autre user que vous sans droit"
|
user_request,
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
)[0]):
|
||||||
|
return False, (u"Vous ne pouvez pas éditer une machine "
|
||||||
|
"d'un autre user que vous sans droit")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def can_delete(self, user_request, *args, **kwargs):
|
def can_delete(self, user_request, *args, **kwargs):
|
||||||
|
@ -871,26 +944,32 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model):
|
||||||
:param user_request: Utilisateur qui fait la requête
|
:param user_request: Utilisateur qui fait la requête
|
||||||
:return: soit True, soit False avec la raison de l'échec"""
|
:return: soit True, soit False avec la raison de l'échec"""
|
||||||
if self.machine.user != user_request:
|
if self.machine.user != user_request:
|
||||||
if not user_request.has_perm('machines.change_interface') or not self.machine.user.can_edit(user_request, *args, **kwargs)[0]:
|
if (not user_request.has_perm('machines.change_interface') or
|
||||||
return False, u"Vous ne pouvez pas éditer une machine\
|
not self.machine.user.can_edit(
|
||||||
d'un autre user que vous sans droit"
|
user_request,
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
)[0]):
|
||||||
|
return False, (u"Vous ne pouvez pas éditer une machine "
|
||||||
|
"d'un autre user que vous sans droit")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
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 ou qu'elle appartient à l'user
|
droit view objet ou qu'elle appartient à l'user
|
||||||
:param self: instance interface à voir
|
:param self: instance interface à voir
|
||||||
:param user_request: instance user qui fait l'edition
|
:param user_request: instance user qui fait l'edition
|
||||||
:return: True ou False avec la raison de l'échec le cas échéant"""
|
:return: True ou False avec la raison de l'échec le cas échéant"""
|
||||||
if not user_request.has_perm('machines.view_interface') and self.machine.user != user_request:
|
if (not user_request.has_perm('machines.view_interface') and
|
||||||
return False, u"Vous n'avez pas le droit de voir des machines autre\
|
self.machine.user != user_request):
|
||||||
que les vôtres"
|
return False, (u"Vous n'avez pas le droit de voir des machines "
|
||||||
|
"autre que les vôtres")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Interface, self).__init__(*args, **kwargs)
|
super(Interface, self).__init__(*args, **kwargs)
|
||||||
self.field_permissions = {
|
self.field_permissions = {
|
||||||
'machine' : self.can_change_machine,
|
'machine': self.can_change_machine,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -915,22 +994,29 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin,models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
|
""" A list of IPv6 """
|
||||||
PRETTY_NAME = 'Enregistrements Ipv6 des machines'
|
PRETTY_NAME = 'Enregistrements Ipv6 des machines'
|
||||||
|
|
||||||
ipv6 = models.GenericIPAddressField(
|
ipv6 = models.GenericIPAddressField(
|
||||||
protocol='IPv6',
|
protocol='IPv6',
|
||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
interface = models.ForeignKey('Interface', on_delete=models.CASCADE, related_name='ipv6list')
|
interface = models.ForeignKey(
|
||||||
|
'Interface',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='ipv6list'
|
||||||
|
)
|
||||||
slaac_ip = models.BooleanField(default=False)
|
slaac_ip = models.BooleanField(default=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
("view_ipv6list", "Peut voir un objet ipv6"),
|
("view_ipv6list", "Peut voir un objet ipv6"),
|
||||||
("change_ipv6list_slaac_ip", "Peut changer la valeur slaac sur une ipv6"),
|
("change_ipv6list_slaac_ip",
|
||||||
|
"Peut changer la valeur slaac sur une ipv6"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def can_create(user_request, interfaceid, *args, **kwargs):
|
@staticmethod
|
||||||
|
def can_create(user_request, interfaceid, *_args, **_kwargs):
|
||||||
"""Verifie que l'user a les bons droits infra pour créer
|
"""Verifie que l'user a les bons droits infra pour créer
|
||||||
une ipv6, ou possède l'interface associée
|
une ipv6, ou possède l'interface associée
|
||||||
:param interfaceid: Id de l'interface associée à cet objet domain
|
:param interfaceid: Id de l'interface associée à cet objet domain
|
||||||
|
@ -947,8 +1033,10 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_change_slaac_ip(user_request, *args, **kwargs):
|
def can_change_slaac_ip(user_request, *_args, **_kwargs):
|
||||||
return user_request.has_perm('machines.change_ipv6list_slaac_ip'), "Droit requis pour changer la valeur slaac ip"
|
""" Check if a user can change the slaac value """
|
||||||
|
return (user_request.has_perm('machines.change_ipv6list_slaac_ip'),
|
||||||
|
"Droit requis pour changer la valeur slaac ip")
|
||||||
|
|
||||||
def can_edit(self, user_request, *args, **kwargs):
|
def can_edit(self, user_request, *args, **kwargs):
|
||||||
"""Verifie que l'user a les bons droits infra pour editer
|
"""Verifie que l'user a les bons droits infra pour editer
|
||||||
|
@ -957,9 +1045,14 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
:param user_request: Utilisateur qui fait la requête
|
:param user_request: Utilisateur qui fait la requête
|
||||||
:return: soit True, soit False avec la raison de l'échec"""
|
:return: soit True, soit False avec la raison de l'échec"""
|
||||||
if self.interface.machine.user != user_request:
|
if self.interface.machine.user != user_request:
|
||||||
if not user_request.has_perm('machines.change_ipv6list') or not self.interface.machine.user.can_edit(user_request, *args, **kwargs)[0]:
|
if (not user_request.has_perm('machines.change_ipv6list') or
|
||||||
return False, u"Vous ne pouvez pas éditer une machine\
|
not self.interface.machine.user.can_edit(
|
||||||
d'un autre user que vous sans droit"
|
user_request,
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
)[0]):
|
||||||
|
return False, (u"Vous ne pouvez pas éditer une machine "
|
||||||
|
"d'un autre user que vous sans droit")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def can_delete(self, user_request, *args, **kwargs):
|
def can_delete(self, user_request, *args, **kwargs):
|
||||||
|
@ -969,26 +1062,32 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
:param user_request: Utilisateur qui fait la requête
|
:param user_request: Utilisateur qui fait la requête
|
||||||
:return: soit True, soit False avec la raison de l'échec"""
|
:return: soit True, soit False avec la raison de l'échec"""
|
||||||
if self.interface.machine.user != user_request:
|
if self.interface.machine.user != user_request:
|
||||||
if not user_request.has_perm('machines.change_ipv6list') or not self.interface.machine.user.can_edit(user_request, *args, **kwargs)[0]:
|
if (not user_request.has_perm('machines.change_ipv6list') or
|
||||||
return False, u"Vous ne pouvez pas éditer une machine\
|
not self.interface.machine.user.can_edit(
|
||||||
d'un autre user que vous sans droit"
|
user_request,
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
)[0]):
|
||||||
|
return False, (u"Vous ne pouvez pas éditer une machine "
|
||||||
|
"d'un autre user que vous sans droit")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
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 ou qu'elle appartient à l'user
|
droit view objet ou qu'elle appartient à l'user
|
||||||
:param self: instance interface à voir
|
:param self: instance interface à voir
|
||||||
:param user_request: instance user qui fait l'edition
|
:param user_request: instance user qui fait l'edition
|
||||||
:return: True ou False avec la raison de l'échec le cas échéant"""
|
:return: True ou False avec la raison de l'échec le cas échéant"""
|
||||||
if not user_request.has_perm('machines.view_ipv6list') and self.interface.machine.user != user_request:
|
if (not user_request.has_perm('machines.view_ipv6list') and
|
||||||
return False, u"Vous n'avez pas le droit de voir des machines autre\
|
self.interface.machine.user != user_request):
|
||||||
que les vôtres"
|
return False, (u"Vous n'avez pas le droit de voir des machines "
|
||||||
|
"autre que les vôtres")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(Ipv6List, self).__init__(*args, **kwargs)
|
super(Ipv6List, self).__init__(*args, **kwargs)
|
||||||
self.field_permissions = {
|
self.field_permissions = {
|
||||||
'slaac_ip' : self.can_change_slaac_ip,
|
'slaac_ip': self.can_change_slaac_ip,
|
||||||
}
|
}
|
||||||
|
|
||||||
def check_and_replace_prefix(self, prefix=None):
|
def check_and_replace_prefix(self, prefix=None):
|
||||||
|
@ -996,17 +1095,27 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
|
||||||
prefix_v6 = prefix or self.interface.type.ip_type.prefix_v6
|
prefix_v6 = prefix or self.interface.type.ip_type.prefix_v6
|
||||||
if not prefix_v6:
|
if not prefix_v6:
|
||||||
return
|
return
|
||||||
if IPv6Address(self.ipv6).exploded[:20] != IPv6Address(prefix_v6).exploded[:20]:
|
if (IPv6Address(self.ipv6).exploded[:20] !=
|
||||||
self.ipv6 = IPv6Address(IPv6Address(prefix_v6).exploded[:20] + IPv6Address(self.ipv6).exploded[20:])
|
IPv6Address(prefix_v6).exploded[:20]):
|
||||||
|
self.ipv6 = IPv6Address(
|
||||||
|
IPv6Address(prefix_v6).exploded[:20] +
|
||||||
|
IPv6Address(self.ipv6).exploded[20:]
|
||||||
|
)
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def clean(self, *args, **kwargs):
|
def clean(self, *args, **kwargs):
|
||||||
if self.slaac_ip and Ipv6List.objects.filter(interface=self.interface, slaac_ip=True).exclude(id=self.id):
|
if self.slaac_ip and (Ipv6List.objects
|
||||||
|
.filter(interface=self.interface, slaac_ip=True)
|
||||||
|
.exclude(id=self.id)):
|
||||||
raise ValidationError("Une ip slaac est déjà enregistrée")
|
raise ValidationError("Une ip slaac est déjà enregistrée")
|
||||||
prefix_v6 = self.interface.type.ip_type.prefix_v6
|
prefix_v6 = self.interface.type.ip_type.prefix_v6
|
||||||
if prefix_v6:
|
if prefix_v6:
|
||||||
if IPv6Address(self.ipv6).exploded[:20] != IPv6Address(prefix_v6).exploded[:20]:
|
if (IPv6Address(self.ipv6).exploded[:20] !=
|
||||||
raise ValidationError("Le prefixv6 est incorrect et ne correspond pas au type associé à la machine")
|
IPv6Address(prefix_v6).exploded[:20]):
|
||||||
|
raise ValidationError(
|
||||||
|
"Le prefixv6 est incorrect et ne correspond pas au type "
|
||||||
|
"associé à la machine"
|
||||||
|
)
|
||||||
super(Ipv6List, self).clean(*args, **kwargs)
|
super(Ipv6List, self).clean(*args, **kwargs)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
@ -1072,7 +1181,7 @@ class Domain(RevMixin, AclMixin, models.Model):
|
||||||
if self.cname == self:
|
if self.cname == self:
|
||||||
raise ValidationError("On ne peut créer un cname sur lui même")
|
raise ValidationError("On ne peut créer un cname sur lui même")
|
||||||
HOSTNAME_LABEL_PATTERN = re.compile(
|
HOSTNAME_LABEL_PATTERN = re.compile(
|
||||||
"(?!-)[A-Z\d-]+(?<!-)$",
|
r"(?!-)[A-Z\d-]+(?<!-)$",
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
dns = self.name.lower()
|
dns = self.name.lower()
|
||||||
|
@ -1089,7 +1198,10 @@ class Domain(RevMixin, AclMixin, models.Model):
|
||||||
def dns_entry(self):
|
def dns_entry(self):
|
||||||
""" Une entrée DNS"""
|
""" Une entrée DNS"""
|
||||||
if self.cname:
|
if self.cname:
|
||||||
return str(self.name).ljust(15) + " IN CNAME " + str(self.cname) + "."
|
return "{name} IN CNAME {cname}.".format(
|
||||||
|
name=str(self.name).ljust(15),
|
||||||
|
cname=str(self.cname)
|
||||||
|
)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
""" Empèche le save sans extension valide.
|
""" Empèche le save sans extension valide.
|
||||||
|
@ -1111,7 +1223,8 @@ class Domain(RevMixin, AclMixin, models.Model):
|
||||||
else:
|
else:
|
||||||
return self.cname.get_parent_interface()
|
return self.cname.get_parent_interface()
|
||||||
|
|
||||||
def can_create(user_request, interfaceid, *args, **kwargs):
|
@staticmethod
|
||||||
|
def can_create(user_request, interfaceid, *_args, **_kwargs):
|
||||||
"""Verifie que l'user a les bons droits infra pour créer
|
"""Verifie que l'user a les bons droits infra pour créer
|
||||||
un domain, ou possède l'interface associée
|
un domain, ou possède l'interface associée
|
||||||
:param interfaceid: Id de l'interface associée à cet objet domain
|
:param interfaceid: Id de l'interface associée à cet objet domain
|
||||||
|
@ -1122,54 +1235,58 @@ class Domain(RevMixin, AclMixin, models.Model):
|
||||||
except Interface.DoesNotExist:
|
except Interface.DoesNotExist:
|
||||||
return False, u"Interface inexistante"
|
return False, u"Interface inexistante"
|
||||||
if not user_request.has_perm('machines.add_domain'):
|
if not user_request.has_perm('machines.add_domain'):
|
||||||
max_lambdauser_aliases = preferences.models.OptionalMachine.get_cached_value('max_lambdauser_aliases')
|
max_lambdauser_aliases = (preferences.models.OptionalMachine
|
||||||
|
.get_cached_value(
|
||||||
|
'max_lambdauser_aliases'
|
||||||
|
))
|
||||||
if interface.machine.user != user_request:
|
if interface.machine.user != user_request:
|
||||||
return False, u"Vous ne pouvez pas ajouter un alias à une\
|
return False, (u"Vous ne pouvez pas ajouter un alias à une "
|
||||||
machine d'un autre user que vous sans droit"
|
"machine d'un autre user que vous sans droit")
|
||||||
if Domain.objects.filter(
|
if Domain.objects.filter(
|
||||||
cname__in=Domain.objects.filter(
|
cname__in=Domain.objects.filter(
|
||||||
interface_parent__in=interface.machine.user.user_interfaces()
|
interface_parent__in=(interface.machine.user
|
||||||
|
.user_interfaces())
|
||||||
)
|
)
|
||||||
).count() >= max_lambdauser_aliases:
|
).count() >= max_lambdauser_aliases:
|
||||||
return False, u"Vous avez atteint le maximum d'alias\
|
return False, (u"Vous avez atteint le maximum d'alias "
|
||||||
autorisés que vous pouvez créer vous même (%s) "\
|
"autorisés que vous pouvez créer vous même "
|
||||||
% max_lambdauser_aliases
|
"(%s) " % max_lambdauser_aliases)
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
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 domain
|
cette instance domain
|
||||||
:param self: Instance domain à editer
|
:param self: Instance domain à editer
|
||||||
:param user_request: Utilisateur qui fait la requête
|
:param user_request: Utilisateur qui fait la requête
|
||||||
:return: soit True, soit False avec la raison de l'échec"""
|
:return: soit True, soit False avec la raison de l'échec"""
|
||||||
if not user_request.has_perm('machines.change_domain') and\
|
if (not user_request.has_perm('machines.change_domain') and
|
||||||
self.get_source_interface.machine.user != user_request:
|
self.get_source_interface.machine.user != user_request):
|
||||||
return False, u"Vous ne pouvez pas editer un alias à une machine\
|
return False, (u"Vous ne pouvez pas editer un alias à une machine "
|
||||||
d'un autre user que vous sans droit"
|
"d'un autre user que vous sans droit")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def can_delete(self, user_request, *args, **kwargs):
|
def can_delete(self, user_request, *_args, **_kwargs):
|
||||||
"""Verifie que l'user a les bons droits delete object pour del
|
"""Verifie que l'user a les bons droits delete object pour del
|
||||||
cette instance domain, ou qu'elle lui appartient
|
cette instance domain, ou qu'elle lui appartient
|
||||||
:param self: Instance domain à del
|
:param self: Instance domain à del
|
||||||
:param user_request: Utilisateur qui fait la requête
|
:param user_request: Utilisateur qui fait la requête
|
||||||
:return: soit True, soit False avec la raison de l'échec"""
|
:return: soit True, soit False avec la raison de l'échec"""
|
||||||
if not user_request.has_perm('machines.delete_domain') and\
|
if (not user_request.has_perm('machines.delete_domain') and
|
||||||
self.get_source_interface.machine.user != user_request:
|
self.get_source_interface.machine.user != user_request):
|
||||||
return False, u"Vous ne pouvez pas supprimer un alias à une machine\
|
return False, (u"Vous ne pouvez pas supprimer un alias à une "
|
||||||
d'un autre user que vous sans droit"
|
"machine d'un autre user que vous sans droit")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
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 ou qu'elle appartient à l'user
|
droit view objet ou qu'elle appartient à l'user
|
||||||
:param self: instance domain à voir
|
:param self: instance domain à voir
|
||||||
:param user_request: instance user qui fait l'edition
|
:param user_request: instance user qui fait l'edition
|
||||||
:return: True ou False avec la raison de l'échec le cas échéant"""
|
:return: True ou False avec la raison de l'échec le cas échéant"""
|
||||||
if not user_request.has_perm('machines.view_domain') and\
|
if (not user_request.has_perm('machines.view_domain') and
|
||||||
self.get_source_interface.machine.user != user_request:
|
self.get_source_interface.machine.user != user_request):
|
||||||
return False, u"Vous n'avez pas le droit de voir des machines autre\
|
return False, (u"Vous n'avez pas le droit de voir des machines "
|
||||||
que les vôtres"
|
"autre que les vôtres")
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -1177,6 +1294,7 @@ class Domain(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
|
|
||||||
class IpList(RevMixin, AclMixin, models.Model):
|
class IpList(RevMixin, AclMixin, models.Model):
|
||||||
|
""" A list of IPv4 """
|
||||||
PRETTY_NAME = "Addresses ipv4"
|
PRETTY_NAME = "Addresses ipv4"
|
||||||
|
|
||||||
ipv4 = models.GenericIPAddressField(protocol='IPv4', unique=True)
|
ipv4 = models.GenericIPAddressField(protocol='IPv4', unique=True)
|
||||||
|
@ -1307,15 +1425,15 @@ class OuverturePortList(RevMixin, AclMixin, models.Model):
|
||||||
("view_ouvertureportlist", "Peut voir un objet ouvertureport"),
|
("view_ouvertureportlist", "Peut voir un objet ouvertureport"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def can_delete(self, user_request, *args, **kwargs):
|
def can_delete(self, user_request, *_args, **_kwargs):
|
||||||
"""Verifie que l'user a les bons droits bureau pour delete
|
"""Verifie que l'user a les bons droits bureau pour delete
|
||||||
cette instance ouvertureportlist
|
cette instance ouvertureportlist
|
||||||
:param self: Instance ouvertureportlist à delete
|
:param self: Instance ouvertureportlist à delete
|
||||||
:param user_request: Utilisateur qui fait la requête
|
:param user_request: Utilisateur qui fait la requête
|
||||||
:return: soit True, soit False avec la raison de l'échec"""
|
:return: soit True, soit False avec la raison de l'échec"""
|
||||||
if not user_request.has_perm('machines.delete_ouvertureportlist'):
|
if not user_request.has_perm('machines.delete_ouvertureportlist'):
|
||||||
return False, u"Vous n'avez pas le droit de supprimer une ouverture\
|
return False, (u"Vous n'avez pas le droit de supprimer une "
|
||||||
de port"
|
"ouverture de port")
|
||||||
if self.interface_set.all():
|
if self.interface_set.all():
|
||||||
return False, u"Cette liste de ports est utilisée"
|
return False, u"Cette liste de ports est utilisée"
|
||||||
return True, None
|
return True, None
|
||||||
|
@ -1401,7 +1519,7 @@ class OuverturePort(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Machine)
|
@receiver(post_save, sender=Machine)
|
||||||
def machine_post_save(sender, **kwargs):
|
def machine_post_save(**kwargs):
|
||||||
"""Synchronisation ldap et régen parefeu/dhcp lors de la modification
|
"""Synchronisation ldap et régen parefeu/dhcp lors de la modification
|
||||||
d'une machine"""
|
d'une machine"""
|
||||||
user = kwargs['instance'].user
|
user = kwargs['instance'].user
|
||||||
|
@ -1411,7 +1529,7 @@ def machine_post_save(sender, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Machine)
|
@receiver(post_delete, sender=Machine)
|
||||||
def machine_post_delete(sender, **kwargs):
|
def machine_post_delete(**kwargs):
|
||||||
"""Synchronisation ldap et régen parefeu/dhcp lors de la suppression
|
"""Synchronisation ldap et régen parefeu/dhcp lors de la suppression
|
||||||
d'une machine"""
|
d'une machine"""
|
||||||
machine = kwargs['instance']
|
machine = kwargs['instance']
|
||||||
|
@ -1422,7 +1540,7 @@ def machine_post_delete(sender, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Interface)
|
@receiver(post_save, sender=Interface)
|
||||||
def interface_post_save(sender, **kwargs):
|
def interface_post_save(**kwargs):
|
||||||
"""Synchronisation ldap et régen parefeu/dhcp lors de la modification
|
"""Synchronisation ldap et régen parefeu/dhcp lors de la modification
|
||||||
d'une interface"""
|
d'une interface"""
|
||||||
interface = kwargs['instance']
|
interface = kwargs['instance']
|
||||||
|
@ -1435,7 +1553,7 @@ def interface_post_save(sender, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Interface)
|
@receiver(post_delete, sender=Interface)
|
||||||
def interface_post_delete(sender, **kwargs):
|
def interface_post_delete(**kwargs):
|
||||||
"""Synchronisation ldap et régen parefeu/dhcp lors de la suppression
|
"""Synchronisation ldap et régen parefeu/dhcp lors de la suppression
|
||||||
d'une interface"""
|
d'une interface"""
|
||||||
interface = kwargs['instance']
|
interface = kwargs['instance']
|
||||||
|
@ -1444,7 +1562,7 @@ def interface_post_delete(sender, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=IpType)
|
@receiver(post_save, sender=IpType)
|
||||||
def iptype_post_save(sender, **kwargs):
|
def iptype_post_save(**kwargs):
|
||||||
"""Generation des objets ip après modification d'un range ip"""
|
"""Generation des objets ip après modification d'un range ip"""
|
||||||
iptype = kwargs['instance']
|
iptype = kwargs['instance']
|
||||||
iptype.gen_ip_range()
|
iptype.gen_ip_range()
|
||||||
|
@ -1452,7 +1570,7 @@ def iptype_post_save(sender, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=MachineType)
|
@receiver(post_save, sender=MachineType)
|
||||||
def machinetype_post_save(sender, **kwargs):
|
def machinetype_post_save(**kwargs):
|
||||||
"""Mise à jour des interfaces lorsque changement d'attribution
|
"""Mise à jour des interfaces lorsque changement d'attribution
|
||||||
d'une machinetype (changement iptype parent)"""
|
d'une machinetype (changement iptype parent)"""
|
||||||
machinetype = kwargs['instance']
|
machinetype = kwargs['instance']
|
||||||
|
@ -1461,85 +1579,84 @@ def machinetype_post_save(sender, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Domain)
|
@receiver(post_save, sender=Domain)
|
||||||
def domain_post_save(sender, **kwargs):
|
def domain_post_save(**_kwargs):
|
||||||
"""Regeneration dns après modification d'un domain object"""
|
"""Regeneration dns après modification d'un domain object"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Domain)
|
@receiver(post_delete, sender=Domain)
|
||||||
def domain_post_delete(sender, **kwargs):
|
def domain_post_delete(**_kwargs):
|
||||||
"""Regeneration dns après suppression d'un domain object"""
|
"""Regeneration dns après suppression d'un domain object"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Extension)
|
@receiver(post_save, sender=Extension)
|
||||||
def extension_post_save(sender, **kwargs):
|
def extension_post_save(**_kwargs):
|
||||||
"""Regeneration dns après modification d'une extension"""
|
"""Regeneration dns après modification d'une extension"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Extension)
|
@receiver(post_delete, sender=Extension)
|
||||||
def extension_post_selete(sender, **kwargs):
|
def extension_post_selete(**_kwargs):
|
||||||
"""Regeneration dns après suppression d'une extension"""
|
"""Regeneration dns après suppression d'une extension"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=SOA)
|
@receiver(post_save, sender=SOA)
|
||||||
def soa_post_save(sender, **kwargs):
|
def soa_post_save(**_kwargs):
|
||||||
"""Regeneration dns après modification d'un SOA"""
|
"""Regeneration dns après modification d'un SOA"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=SOA)
|
@receiver(post_delete, sender=SOA)
|
||||||
def soa_post_delete(sender, **kwargs):
|
def soa_post_delete(**_kwargs):
|
||||||
"""Regeneration dns après suppresson d'un SOA"""
|
"""Regeneration dns après suppresson d'un SOA"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Mx)
|
@receiver(post_save, sender=Mx)
|
||||||
def mx_post_save(sender, **kwargs):
|
def mx_post_save(**_kwargs):
|
||||||
"""Regeneration dns après modification d'un MX"""
|
"""Regeneration dns après modification d'un MX"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Mx)
|
@receiver(post_delete, sender=Mx)
|
||||||
def mx_post_delete(sender, **kwargs):
|
def mx_post_delete(**_kwargs):
|
||||||
"""Regeneration dns après suppresson d'un MX"""
|
"""Regeneration dns après suppresson d'un MX"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Ns)
|
@receiver(post_save, sender=Ns)
|
||||||
def ns_post_save(sender, **kwargs):
|
def ns_post_save(**_kwargs):
|
||||||
"""Regeneration dns après modification d'un NS"""
|
"""Regeneration dns après modification d'un NS"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Ns)
|
@receiver(post_delete, sender=Ns)
|
||||||
def ns_post_delete(sender, **kwargs):
|
def ns_post_delete(**_kwargs):
|
||||||
"""Regeneration dns après modification d'un NS"""
|
"""Regeneration dns après modification d'un NS"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Txt)
|
@receiver(post_save, sender=Txt)
|
||||||
def text_post_save(sender, **kwargs):
|
def text_post_save(**_kwargs):
|
||||||
"""Regeneration dns après modification d'un TXT"""
|
"""Regeneration dns après modification d'un TXT"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Txt)
|
@receiver(post_delete, sender=Txt)
|
||||||
def text_post_delete(sender, **kwargs):
|
def text_post_delete(**_kwargs):
|
||||||
"""Regeneration dns après modification d'un TX"""
|
"""Regeneration dns après modification d'un TX"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Srv)
|
@receiver(post_save, sender=Srv)
|
||||||
def srv_post_save(sender, **kwargs):
|
def srv_post_save(**_kwargs):
|
||||||
"""Regeneration dns après modification d'un SRV"""
|
"""Regeneration dns après modification d'un SRV"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Srv)
|
@receiver(post_delete, sender=Srv)
|
||||||
def srv_post_delete(sender, **kwargs):
|
def srv_post_delete(**_kwargs):
|
||||||
"""Regeneration dns après modification d'un SRV"""
|
"""Regeneration dns après modification d'un SRV"""
|
||||||
regen('dns')
|
regen('dns')
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,11 @@
|
||||||
# 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.
|
||||||
|
|
||||||
#Augustin Lemesle
|
# Augustin Lemesle
|
||||||
|
"""machines.serializers
|
||||||
|
Serializers for the Machines app
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from machines.models import (
|
from machines.models import (
|
||||||
|
@ -29,28 +33,30 @@ from machines.models import (
|
||||||
IpType,
|
IpType,
|
||||||
Extension,
|
Extension,
|
||||||
IpList,
|
IpList,
|
||||||
MachineType,
|
|
||||||
Domain,
|
Domain,
|
||||||
Txt,
|
Txt,
|
||||||
Mx,
|
Mx,
|
||||||
Srv,
|
Srv,
|
||||||
Service_link,
|
Service_link,
|
||||||
Ns,
|
Ns,
|
||||||
OuverturePortList,
|
|
||||||
OuverturePort,
|
OuverturePort,
|
||||||
Ipv6List
|
Ipv6List
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class IpTypeField(serializers.RelatedField):
|
class IpTypeField(serializers.RelatedField):
|
||||||
"""Serialisation d'une iptype, renvoie son evaluation str"""
|
""" Serializer for an IpType object field """
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return value.type
|
return value.type
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class IpListSerializer(serializers.ModelSerializer):
|
class IpListSerializer(serializers.ModelSerializer):
|
||||||
"""Serialisation d'une iplist, ip_type etant une foreign_key,
|
""" Serializer for an Ipv4List obejct using the IpType serialization """
|
||||||
on evalue sa methode str"""
|
|
||||||
ip_type = IpTypeField(read_only=True)
|
ip_type = IpTypeField(read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -59,16 +65,19 @@ class IpListSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class Ipv6ListSerializer(serializers.ModelSerializer):
|
class Ipv6ListSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serializer for an Ipv6List object """
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Ipv6List
|
model = Ipv6List
|
||||||
fields = ('ipv6', 'slaac_ip')
|
fields = ('ipv6', 'slaac_ip')
|
||||||
|
|
||||||
|
|
||||||
class InterfaceSerializer(serializers.ModelSerializer):
|
class InterfaceSerializer(serializers.ModelSerializer):
|
||||||
"""Serialisation d'une interface, ipv4, domain et extension sont
|
""" Serializer for an Interface object. Use SerializerMethodField
|
||||||
des foreign_key, on les override et on les evalue avec des fonctions
|
to get ForeignKey values """
|
||||||
get_..."""
|
|
||||||
ipv4 = IpListSerializer(read_only=True)
|
ipv4 = IpListSerializer(read_only=True)
|
||||||
|
# TODO : use serializer.RelatedField to avoid duplicate code
|
||||||
mac_address = serializers.SerializerMethodField('get_macaddress')
|
mac_address = serializers.SerializerMethodField('get_macaddress')
|
||||||
domain = serializers.SerializerMethodField('get_dns')
|
domain = serializers.SerializerMethodField('get_dns')
|
||||||
extension = serializers.SerializerMethodField('get_interface_extension')
|
extension = serializers.SerializerMethodField('get_interface_extension')
|
||||||
|
@ -77,20 +86,29 @@ class InterfaceSerializer(serializers.ModelSerializer):
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = ('ipv4', 'mac_address', 'domain', 'extension')
|
fields = ('ipv4', 'mac_address', 'domain', 'extension')
|
||||||
|
|
||||||
def get_dns(self, obj):
|
@staticmethod
|
||||||
|
def get_dns(obj):
|
||||||
|
""" The name of the associated DNS object """
|
||||||
return obj.domain.name
|
return obj.domain.name
|
||||||
|
|
||||||
def get_interface_extension(self, obj):
|
@staticmethod
|
||||||
|
def get_interface_extension(obj):
|
||||||
|
""" The name of the associated Interface object """
|
||||||
return obj.domain.extension.name
|
return obj.domain.extension.name
|
||||||
|
|
||||||
def get_macaddress(self, obj):
|
@staticmethod
|
||||||
|
def get_macaddress(obj):
|
||||||
|
""" The string representation of the associated MAC address """
|
||||||
return str(obj.mac_address)
|
return str(obj.mac_address)
|
||||||
|
|
||||||
|
|
||||||
class FullInterfaceSerializer(serializers.ModelSerializer):
|
class FullInterfaceSerializer(serializers.ModelSerializer):
|
||||||
"""Serialisation complete d'une interface avec les ipv6 en plus"""
|
""" Serializer for an Interface obejct. Use SerializerMethodField
|
||||||
|
to get ForeignKey values """
|
||||||
|
|
||||||
ipv4 = IpListSerializer(read_only=True)
|
ipv4 = IpListSerializer(read_only=True)
|
||||||
ipv6 = Ipv6ListSerializer(read_only=True, many=True)
|
ipv6 = Ipv6ListSerializer(read_only=True, many=True)
|
||||||
|
# TODO : use serializer.RelatedField to avoid duplicate code
|
||||||
mac_address = serializers.SerializerMethodField('get_macaddress')
|
mac_address = serializers.SerializerMethodField('get_macaddress')
|
||||||
domain = serializers.SerializerMethodField('get_dns')
|
domain = serializers.SerializerMethodField('get_dns')
|
||||||
extension = serializers.SerializerMethodField('get_interface_extension')
|
extension = serializers.SerializerMethodField('get_interface_extension')
|
||||||
|
@ -99,26 +117,36 @@ class FullInterfaceSerializer(serializers.ModelSerializer):
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = ('ipv4', 'ipv6', 'mac_address', 'domain', 'extension')
|
fields = ('ipv4', 'ipv6', 'mac_address', 'domain', 'extension')
|
||||||
|
|
||||||
def get_dns(self, obj):
|
@staticmethod
|
||||||
|
def get_dns(obj):
|
||||||
|
""" The name of the associated DNS object """
|
||||||
return obj.domain.name
|
return obj.domain.name
|
||||||
|
|
||||||
def get_interface_extension(self, obj):
|
@staticmethod
|
||||||
|
def get_interface_extension(obj):
|
||||||
|
""" The name of the associated Extension object """
|
||||||
return obj.domain.extension.name
|
return obj.domain.extension.name
|
||||||
|
|
||||||
def get_macaddress(self, obj):
|
@staticmethod
|
||||||
|
def get_macaddress(obj):
|
||||||
|
""" The string representation of the associated MAC address """
|
||||||
return str(obj.mac_address)
|
return str(obj.mac_address)
|
||||||
|
|
||||||
|
|
||||||
class ExtensionNameField(serializers.RelatedField):
|
class ExtensionNameField(serializers.RelatedField):
|
||||||
"""Evaluation str d'un objet extension (.example.org)"""
|
""" Serializer for Extension object field """
|
||||||
|
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return value.name
|
return value.name
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TypeSerializer(serializers.ModelSerializer):
|
class TypeSerializer(serializers.ModelSerializer):
|
||||||
"""Serialisation d'un iptype : extension et la liste des
|
""" Serializer for an IpType object. Use SerializerMethodField to
|
||||||
ouvertures de port son evalués en get_... etant des
|
get ForeignKey values. Infos about the general port policy is added """
|
||||||
foreign_key ou des relations manytomany"""
|
|
||||||
extension = ExtensionNameField(read_only=True)
|
extension = ExtensionNameField(read_only=True)
|
||||||
ouverture_ports_tcp_in = serializers\
|
ouverture_ports_tcp_in = serializers\
|
||||||
.SerializerMethodField('get_port_policy_input_tcp')
|
.SerializerMethodField('get_port_policy_input_tcp')
|
||||||
|
@ -136,7 +164,10 @@ class TypeSerializer(serializers.ModelSerializer):
|
||||||
'ouverture_ports_tcp_in', 'ouverture_ports_tcp_out',
|
'ouverture_ports_tcp_in', 'ouverture_ports_tcp_out',
|
||||||
'ouverture_ports_udp_in', 'ouverture_ports_udp_out',)
|
'ouverture_ports_udp_in', 'ouverture_ports_udp_out',)
|
||||||
|
|
||||||
def get_port_policy(self, obj, protocole, io):
|
@staticmethod
|
||||||
|
def get_port_policy(obj, protocole, io):
|
||||||
|
""" Generic utility function to get the policy for a given
|
||||||
|
port, protocole and IN or OUT """
|
||||||
if obj.ouverture_ports is None:
|
if obj.ouverture_ports is None:
|
||||||
return []
|
return []
|
||||||
return map(
|
return map(
|
||||||
|
@ -174,14 +205,20 @@ class ExtensionSerializer(serializers.ModelSerializer):
|
||||||
model = Extension
|
model = Extension
|
||||||
fields = ('name', 'origin', 'origin_v6', 'zone_entry', 'soa')
|
fields = ('name', 'origin', 'origin_v6', 'zone_entry', 'soa')
|
||||||
|
|
||||||
def get_origin_ip(self, obj):
|
@staticmethod
|
||||||
return getattr(obj.origin, 'ipv4', None)
|
def get_origin_ip(obj):
|
||||||
|
""" The IP of the associated origin for the zone """
|
||||||
|
return obj.origin.ipv4
|
||||||
|
|
||||||
def get_zone_name(self, obj):
|
@staticmethod
|
||||||
|
def get_zone_name(obj):
|
||||||
|
""" The name of the associated zone """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
def get_soa_data(self, obj):
|
@staticmethod
|
||||||
return { 'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param }
|
def get_soa_data(obj):
|
||||||
|
""" The representation of the associated SOA """
|
||||||
|
return {'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param}
|
||||||
|
|
||||||
|
|
||||||
class MxSerializer(serializers.ModelSerializer):
|
class MxSerializer(serializers.ModelSerializer):
|
||||||
|
@ -195,13 +232,19 @@ class MxSerializer(serializers.ModelSerializer):
|
||||||
model = Mx
|
model = Mx
|
||||||
fields = ('zone', 'priority', 'name', 'mx_entry')
|
fields = ('zone', 'priority', 'name', 'mx_entry')
|
||||||
|
|
||||||
def get_entry_name(self, obj):
|
@staticmethod
|
||||||
|
def get_entry_name(obj):
|
||||||
|
""" The name of the DNS MX entry """
|
||||||
return str(obj.name)
|
return str(obj.name)
|
||||||
|
|
||||||
def get_zone_name(self, obj):
|
@staticmethod
|
||||||
|
def get_zone_name(obj):
|
||||||
|
""" The name of the associated zone of the MX record """
|
||||||
return obj.zone.name
|
return obj.zone.name
|
||||||
|
|
||||||
def get_mx_name(self, obj):
|
@staticmethod
|
||||||
|
def get_mx_name(obj):
|
||||||
|
""" The string representation of the entry to add to the DNS """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,10 +258,14 @@ class TxtSerializer(serializers.ModelSerializer):
|
||||||
model = Txt
|
model = Txt
|
||||||
fields = ('zone', 'txt_entry', 'field1', 'field2')
|
fields = ('zone', 'txt_entry', 'field1', 'field2')
|
||||||
|
|
||||||
def get_zone_name(self, obj):
|
@staticmethod
|
||||||
|
def get_zone_name(obj):
|
||||||
|
""" The name of the associated zone """
|
||||||
return str(obj.zone.name)
|
return str(obj.zone.name)
|
||||||
|
|
||||||
def get_txt_name(self, obj):
|
@staticmethod
|
||||||
|
def get_txt_name(obj):
|
||||||
|
""" The string representation of the entry to add to the DNS """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@ -241,10 +288,14 @@ class SrvSerializer(serializers.ModelSerializer):
|
||||||
'srv_entry'
|
'srv_entry'
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_extension_name(self, obj):
|
@staticmethod
|
||||||
|
def get_extension_name(obj):
|
||||||
|
""" The name of the associated extension """
|
||||||
return str(obj.extension.name)
|
return str(obj.extension.name)
|
||||||
|
|
||||||
def get_srv_name(self, obj):
|
@staticmethod
|
||||||
|
def get_srv_name(obj):
|
||||||
|
""" The string representation of the entry to add to the DNS """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@ -259,13 +310,19 @@ class NsSerializer(serializers.ModelSerializer):
|
||||||
model = Ns
|
model = Ns
|
||||||
fields = ('zone', 'ns', 'ns_entry')
|
fields = ('zone', 'ns', 'ns_entry')
|
||||||
|
|
||||||
def get_zone_name(self, obj):
|
@staticmethod
|
||||||
|
def get_zone_name(obj):
|
||||||
|
""" The name of the associated zone """
|
||||||
return obj.zone.name
|
return obj.zone.name
|
||||||
|
|
||||||
def get_domain_name(self, obj):
|
@staticmethod
|
||||||
|
def get_domain_name(obj):
|
||||||
|
""" The name of the associated NS target """
|
||||||
return str(obj.ns)
|
return str(obj.ns)
|
||||||
|
|
||||||
def get_text_name(self, obj):
|
@staticmethod
|
||||||
|
def get_text_name(obj):
|
||||||
|
""" The string representation of the entry to add to the DNS """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@ -280,13 +337,19 @@ class DomainSerializer(serializers.ModelSerializer):
|
||||||
model = Domain
|
model = Domain
|
||||||
fields = ('name', 'extension', 'cname', 'cname_entry')
|
fields = ('name', 'extension', 'cname', 'cname_entry')
|
||||||
|
|
||||||
def get_zone_name(self, obj):
|
@staticmethod
|
||||||
|
def get_zone_name(obj):
|
||||||
|
""" The name of the associated zone """
|
||||||
return obj.extension.name
|
return obj.extension.name
|
||||||
|
|
||||||
def get_alias_name(self, obj):
|
@staticmethod
|
||||||
|
def get_alias_name(obj):
|
||||||
|
""" The name of the associated alias """
|
||||||
return str(obj.cname)
|
return str(obj.cname)
|
||||||
|
|
||||||
def get_cname_name(self, obj):
|
@staticmethod
|
||||||
|
def get_cname_name(obj):
|
||||||
|
""" The name of the associated CNAME target """
|
||||||
return str(obj.dns_entry)
|
return str(obj.dns_entry)
|
||||||
|
|
||||||
|
|
||||||
|
@ -300,13 +363,19 @@ class ServiceServersSerializer(serializers.ModelSerializer):
|
||||||
model = Service_link
|
model = Service_link
|
||||||
fields = ('server', 'service', 'need_regen')
|
fields = ('server', 'service', 'need_regen')
|
||||||
|
|
||||||
def get_server_name(self, obj):
|
@staticmethod
|
||||||
|
def get_server_name(obj):
|
||||||
|
""" The name of the associated server """
|
||||||
return str(obj.server.domain.name)
|
return str(obj.server.domain.name)
|
||||||
|
|
||||||
def get_service_name(self, obj):
|
@staticmethod
|
||||||
|
def get_service_name(obj):
|
||||||
|
""" The name of the service name """
|
||||||
return str(obj.service)
|
return str(obj.service)
|
||||||
|
|
||||||
def get_regen_status(self, obj):
|
@staticmethod
|
||||||
|
def get_regen_status(obj):
|
||||||
|
""" The string representation of the regen status """
|
||||||
return obj.need_regen()
|
return obj.need_regen()
|
||||||
|
|
||||||
|
|
||||||
|
@ -315,24 +384,38 @@ class OuverturePortsSerializer(serializers.Serializer):
|
||||||
ipv4 = serializers.SerializerMethodField()
|
ipv4 = serializers.SerializerMethodField()
|
||||||
ipv6 = serializers.SerializerMethodField()
|
ipv6 = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
""" Creates a new object based on the un-serialized data.
|
||||||
|
Used to implement an abstract inherited method """
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
""" Updates an object based on the un-serialized data.
|
||||||
|
Used to implement an abstract inherited method """
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def get_ipv4():
|
def get_ipv4():
|
||||||
return {i.ipv4.ipv4:
|
""" The representation of the policy for the IPv4 addresses """
|
||||||
{
|
return {
|
||||||
"tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()],
|
i.ipv4.ipv4: {
|
||||||
"tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()],
|
"tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()],
|
||||||
"udp_in":[j.udp_ports_in() for j in i.port_lists.all()],
|
"tcp_out": [j.tcp_ports_out()for j in i.port_lists.all()],
|
||||||
"udp_out":[j.udp_ports_out() for j in i.port_lists.all()],
|
"udp_in": [j.udp_ports_in() for j in i.port_lists.all()],
|
||||||
|
"udp_out": [j.udp_ports_out() for j in i.port_lists.all()],
|
||||||
}
|
}
|
||||||
for i in Interface.objects.all() if i.ipv4
|
for i in Interface.objects.all() if i.ipv4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def get_ipv6():
|
def get_ipv6():
|
||||||
return {i.ipv6:
|
""" The representation of the policy for the IPv6 addresses """
|
||||||
{
|
return {
|
||||||
"tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()],
|
i.ipv6: {
|
||||||
"tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()],
|
"tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()],
|
||||||
"udp_in":[j.udp_ports_in() for j in i.port_lists.all()],
|
"tcp_out": [j.tcp_ports_out()for j in i.port_lists.all()],
|
||||||
"udp_out":[j.udp_ports_out() for j in i.port_lists.all()],
|
"udp_in": [j.udp_ports_in() for j in i.port_lists.all()],
|
||||||
|
"udp_out": [j.udp_ports_out() for j in i.port_lists.all()],
|
||||||
}
|
}
|
||||||
for i in Interface.objects.all() if i.ipv6
|
for i in Interface.objects.all() if i.ipv6
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,10 @@
|
||||||
# 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.
|
||||||
|
"""machines.tests
|
||||||
|
The tests for the API module.
|
||||||
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
# from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
108
machines/urls.py
108
machines/urls.py
|
@ -20,6 +20,9 @@
|
||||||
# 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.
|
||||||
|
"""machines.urls
|
||||||
|
The defined URLs for the Cotisations app
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
@ -28,21 +31,39 @@ import re2o
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^new_machine/(?P<userid>[0-9]+)$', views.new_machine, name='new-machine'),
|
url(r'^new_machine/(?P<userid>[0-9]+)$',
|
||||||
url(r'^edit_interface/(?P<interfaceid>[0-9]+)$', views.edit_interface, name='edit-interface'),
|
views.new_machine,
|
||||||
url(r'^del_machine/(?P<machineid>[0-9]+)$', views.del_machine, name='del-machine'),
|
name='new-machine'),
|
||||||
url(r'^new_interface/(?P<machineid>[0-9]+)$', views.new_interface, name='new-interface'),
|
url(r'^edit_interface/(?P<interfaceid>[0-9]+)$',
|
||||||
url(r'^del_interface/(?P<interfaceid>[0-9]+)$', views.del_interface, name='del-interface'),
|
views.edit_interface,
|
||||||
|
name='edit-interface'),
|
||||||
|
url(r'^del_machine/(?P<machineid>[0-9]+)$',
|
||||||
|
views.del_machine,
|
||||||
|
name='del-machine'),
|
||||||
|
url(r'^new_interface/(?P<machineid>[0-9]+)$',
|
||||||
|
views.new_interface,
|
||||||
|
name='new-interface'),
|
||||||
|
url(r'^del_interface/(?P<interfaceid>[0-9]+)$',
|
||||||
|
views.del_interface,
|
||||||
|
name='del-interface'),
|
||||||
url(r'^add_machinetype/$', views.add_machinetype, name='add-machinetype'),
|
url(r'^add_machinetype/$', views.add_machinetype, name='add-machinetype'),
|
||||||
url(r'^edit_machinetype/(?P<machinetypeid>[0-9]+)$', views.edit_machinetype, name='edit-machinetype'),
|
url(r'^edit_machinetype/(?P<machinetypeid>[0-9]+)$',
|
||||||
|
views.edit_machinetype,
|
||||||
|
name='edit-machinetype'),
|
||||||
url(r'^del_machinetype/$', views.del_machinetype, name='del-machinetype'),
|
url(r'^del_machinetype/$', views.del_machinetype, name='del-machinetype'),
|
||||||
url(r'^index_machinetype/$', views.index_machinetype, name='index-machinetype'),
|
url(r'^index_machinetype/$',
|
||||||
|
views.index_machinetype,
|
||||||
|
name='index-machinetype'),
|
||||||
url(r'^add_iptype/$', views.add_iptype, name='add-iptype'),
|
url(r'^add_iptype/$', views.add_iptype, name='add-iptype'),
|
||||||
url(r'^edit_iptype/(?P<iptypeid>[0-9]+)$', views.edit_iptype, name='edit-iptype'),
|
url(r'^edit_iptype/(?P<iptypeid>[0-9]+)$',
|
||||||
|
views.edit_iptype,
|
||||||
|
name='edit-iptype'),
|
||||||
url(r'^del_iptype/$', views.del_iptype, name='del-iptype'),
|
url(r'^del_iptype/$', views.del_iptype, name='del-iptype'),
|
||||||
url(r'^index_iptype/$', views.index_iptype, name='index-iptype'),
|
url(r'^index_iptype/$', views.index_iptype, name='index-iptype'),
|
||||||
url(r'^add_extension/$', views.add_extension, name='add-extension'),
|
url(r'^add_extension/$', views.add_extension, name='add-extension'),
|
||||||
url(r'^edit_extension/(?P<extensionid>[0-9]+)$', views.edit_extension, name='edit-extension'),
|
url(r'^edit_extension/(?P<extensionid>[0-9]+)$',
|
||||||
|
views.edit_extension,
|
||||||
|
name='edit-extension'),
|
||||||
url(r'^del_extension/$', views.del_extension, name='del-extension'),
|
url(r'^del_extension/$', views.del_extension, name='del-extension'),
|
||||||
url(r'^add_soa/$', views.add_soa, name='add-soa'),
|
url(r'^add_soa/$', views.add_soa, name='add-soa'),
|
||||||
url(r'^edit_soa/(?P<soaid>[0-9]+)$', views.edit_soa, name='edit-soa'),
|
url(r'^edit_soa/(?P<soaid>[0-9]+)$', views.edit_soa, name='edit-soa'),
|
||||||
|
@ -60,16 +81,34 @@ urlpatterns = [
|
||||||
url(r'^edit_srv/(?P<srvid>[0-9]+)$', views.edit_srv, name='edit-srv'),
|
url(r'^edit_srv/(?P<srvid>[0-9]+)$', views.edit_srv, name='edit-srv'),
|
||||||
url(r'^del_srv/$', views.del_srv, name='del-srv'),
|
url(r'^del_srv/$', views.del_srv, name='del-srv'),
|
||||||
url(r'^index_extension/$', views.index_extension, name='index-extension'),
|
url(r'^index_extension/$', views.index_extension, name='index-extension'),
|
||||||
url(r'^add_alias/(?P<interfaceid>[0-9]+)$', views.add_alias, name='add-alias'),
|
url(r'^add_alias/(?P<interfaceid>[0-9]+)$',
|
||||||
url(r'^edit_alias/(?P<domainid>[0-9]+)$', views.edit_alias, name='edit-alias'),
|
views.add_alias,
|
||||||
url(r'^del_alias/(?P<interfaceid>[0-9]+)$', views.del_alias, name='del-alias'),
|
name='add-alias'),
|
||||||
url(r'^index_alias/(?P<interfaceid>[0-9]+)$', views.index_alias, name='index-alias'),
|
url(r'^edit_alias/(?P<domainid>[0-9]+)$',
|
||||||
url(r'^new_ipv6list/(?P<interfaceid>[0-9]+)$', views.new_ipv6list, name='new-ipv6list'),
|
views.edit_alias,
|
||||||
url(r'^edit_ipv6list/(?P<ipv6listid>[0-9]+)$', views.edit_ipv6list, name='edit-ipv6list'),
|
name='edit-alias'),
|
||||||
url(r'^del_ipv6list/(?P<ipv6listid>[0-9]+)$', views.del_ipv6list, name='del-ipv6list'),
|
url(r'^del_alias/(?P<interfaceid>[0-9]+)$',
|
||||||
url(r'^index_ipv6/(?P<interfaceid>[0-9]+)$', views.index_ipv6, name='index-ipv6'),
|
views.del_alias,
|
||||||
|
name='del-alias'),
|
||||||
|
url(r'^index_alias/(?P<interfaceid>[0-9]+)$',
|
||||||
|
views.index_alias,
|
||||||
|
name='index-alias'),
|
||||||
|
url(r'^new_ipv6list/(?P<interfaceid>[0-9]+)$',
|
||||||
|
views.new_ipv6list,
|
||||||
|
name='new-ipv6list'),
|
||||||
|
url(r'^edit_ipv6list/(?P<ipv6listid>[0-9]+)$',
|
||||||
|
views.edit_ipv6list,
|
||||||
|
name='edit-ipv6list'),
|
||||||
|
url(r'^del_ipv6list/(?P<ipv6listid>[0-9]+)$',
|
||||||
|
views.del_ipv6list,
|
||||||
|
name='del-ipv6list'),
|
||||||
|
url(r'^index_ipv6/(?P<interfaceid>[0-9]+)$',
|
||||||
|
views.index_ipv6,
|
||||||
|
name='index-ipv6'),
|
||||||
url(r'^add_service/$', views.add_service, name='add-service'),
|
url(r'^add_service/$', views.add_service, name='add-service'),
|
||||||
url(r'^edit_service/(?P<serviceid>[0-9]+)$', views.edit_service, name='edit-service'),
|
url(r'^edit_service/(?P<serviceid>[0-9]+)$',
|
||||||
|
views.edit_service,
|
||||||
|
name='edit-service'),
|
||||||
url(r'^del_service/$', views.del_service, name='del-service'),
|
url(r'^del_service/$', views.del_service, name='del-service'),
|
||||||
url(r'^index_service/$', views.index_service, name='index-service'),
|
url(r'^index_service/$', views.index_service, name='index-service'),
|
||||||
url(r'^add_vlan/$', views.add_vlan, name='add-vlan'),
|
url(r'^add_vlan/$', views.add_vlan, name='add-vlan'),
|
||||||
|
@ -80,15 +119,15 @@ urlpatterns = [
|
||||||
url(r'^edit_nas/(?P<nasid>[0-9]+)$', views.edit_nas, name='edit-nas'),
|
url(r'^edit_nas/(?P<nasid>[0-9]+)$', views.edit_nas, name='edit-nas'),
|
||||||
url(r'^del_nas/$', views.del_nas, name='del-nas'),
|
url(r'^del_nas/$', views.del_nas, name='del-nas'),
|
||||||
url(r'^index_nas/$', views.index_nas, name='index-nas'),
|
url(r'^index_nas/$', views.index_nas, name='index-nas'),
|
||||||
url(
|
url(r'history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
||||||
r'history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
|
||||||
re2o.views.history,
|
re2o.views.history,
|
||||||
name='history',
|
name='history',
|
||||||
kwargs={'application':'machines'},
|
kwargs={'application': 'machines'}),
|
||||||
),
|
|
||||||
url(r'^$', views.index, name='index'),
|
url(r'^$', views.index, name='index'),
|
||||||
url(r'^rest/mac-ip/$', views.mac_ip, name='mac-ip'),
|
url(r'^rest/mac-ip/$', views.mac_ip, name='mac-ip'),
|
||||||
url(r'^rest/regen-achieved/$', views.regen_achieved, name='regen-achieved'),
|
url(r'^rest/regen-achieved/$',
|
||||||
|
views.regen_achieved,
|
||||||
|
name='regen-achieved'),
|
||||||
url(r'^rest/mac-ip-dns/$', views.mac_ip_dns, name='mac-ip-dns'),
|
url(r'^rest/mac-ip-dns/$', views.mac_ip_dns, name='mac-ip-dns'),
|
||||||
url(r'^rest/alias/$', views.alias, name='alias'),
|
url(r'^rest/alias/$', views.alias, name='alias'),
|
||||||
url(r'^rest/corresp/$', views.corresp, name='corresp'),
|
url(r'^rest/corresp/$', views.corresp, name='corresp'),
|
||||||
|
@ -97,12 +136,21 @@ urlpatterns = [
|
||||||
url(r'^rest/txt/$', views.txt, name='txt'),
|
url(r'^rest/txt/$', views.txt, name='txt'),
|
||||||
url(r'^rest/srv/$', views.srv, name='srv'),
|
url(r'^rest/srv/$', views.srv, name='srv'),
|
||||||
url(r'^rest/zones/$', views.zones, name='zones'),
|
url(r'^rest/zones/$', views.zones, name='zones'),
|
||||||
url(r'^rest/service_servers/$', views.service_servers, name='service-servers'),
|
url(r'^rest/service_servers/$',
|
||||||
url(r'^rest/ouverture_ports/$', views.ouverture_ports, name='ouverture-ports'),
|
views.service_servers,
|
||||||
|
name='service-servers'),
|
||||||
|
url(r'^rest/ouverture_ports/$',
|
||||||
|
views.ouverture_ports,
|
||||||
|
name='ouverture-ports'),
|
||||||
url(r'index_portlist/$', views.index_portlist, name='index-portlist'),
|
url(r'index_portlist/$', views.index_portlist, name='index-portlist'),
|
||||||
url(r'^edit_portlist/(?P<ouvertureportlistid>[0-9]+)$', views.edit_portlist, name='edit-portlist'),
|
url(r'^edit_portlist/(?P<ouvertureportlistid>[0-9]+)$',
|
||||||
url(r'^del_portlist/(?P<ouvertureportlistid>[0-9]+)$', views.del_portlist, name='del-portlist'),
|
views.edit_portlist,
|
||||||
|
name='edit-portlist'),
|
||||||
|
url(r'^del_portlist/(?P<ouvertureportlistid>[0-9]+)$',
|
||||||
|
views.del_portlist,
|
||||||
|
name='del-portlist'),
|
||||||
url(r'^add_portlist/$', views.add_portlist, name='add-portlist'),
|
url(r'^add_portlist/$', views.add_portlist, name='add-portlist'),
|
||||||
url(r'^port_config/(?P<interfaceid>[0-9]+)$', views.configure_ports, name='port-config'),
|
url(r'^port_config/(?P<interfaceid>[0-9]+)$',
|
||||||
|
views.configure_ports,
|
||||||
]
|
name='port-config'),
|
||||||
|
]
|
||||||
|
|
1172
machines/views.py
1172
machines/views.py
File diff suppressed because it is too large
Load diff
|
@ -1,2 +1,28 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2017 Gabriel Détraz
|
||||||
|
# Copyright © 2017 Goulven Kermarec
|
||||||
|
# Copyright © 2017 Augustin Lemesle
|
||||||
|
# Copyright © 2018 Maël Kervella
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
"""preferences
|
||||||
|
The app in charge of storing all the preferences for the local installation
|
||||||
|
"""
|
||||||
|
|
||||||
from .acl import *
|
from .acl import *
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
Here are defined some functions to check acl on the application.
|
Here are defined some functions to check acl on the application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def can_view(user):
|
def can_view(user):
|
||||||
"""Check if an user can view the application.
|
"""Check if an user can view the application.
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,34 @@
|
||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2017 Gabriel Détraz
|
||||||
|
# Copyright © 2017 Goulven Kermarec
|
||||||
|
# Copyright © 2017 Augustin Lemesle
|
||||||
|
# Copyright © 2018 Maël Kervella
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
# App de gestion des machines pour re2o
|
||||||
|
# Gabriel Détraz, Augustin Lemesle
|
||||||
|
# Gplv2
|
||||||
|
"""preferences.aes_field
|
||||||
|
Module defining a AESEncryptedField object that can be used in forms
|
||||||
|
to handle the use of properly encrypting and decrypting AES keys
|
||||||
|
"""
|
||||||
|
|
||||||
import string
|
import string
|
||||||
import binascii
|
import binascii
|
||||||
from random import choice
|
from random import choice
|
||||||
|
@ -10,10 +41,13 @@ EOD = '`%EofD%`' # This should be something that will not occur in strings
|
||||||
|
|
||||||
|
|
||||||
def genstring(length=16, chars=string.printable):
|
def genstring(length=16, chars=string.printable):
|
||||||
|
""" Generate a random string of length `length` and composed of
|
||||||
|
the characters in `chars` """
|
||||||
return ''.join([choice(chars) for i in range(length)])
|
return ''.join([choice(chars) for i in range(length)])
|
||||||
|
|
||||||
|
|
||||||
def encrypt(key, s):
|
def encrypt(key, s):
|
||||||
|
""" AES Encrypt a secret `s` with the key `key` """
|
||||||
obj = AES.new(key)
|
obj = AES.new(key)
|
||||||
datalength = len(s) + len(EOD)
|
datalength = len(s) + len(EOD)
|
||||||
if datalength < 16:
|
if datalength < 16:
|
||||||
|
@ -25,12 +59,15 @@ def encrypt(key, s):
|
||||||
|
|
||||||
|
|
||||||
def decrypt(key, s):
|
def decrypt(key, s):
|
||||||
|
""" AES Decrypt a secret `s` with the key `key` """
|
||||||
obj = AES.new(key)
|
obj = AES.new(key)
|
||||||
ss = obj.decrypt(s)
|
ss = obj.decrypt(s)
|
||||||
return ss.split(bytes(EOD, 'utf-8'))[0]
|
return ss.split(bytes(EOD, 'utf-8'))[0]
|
||||||
|
|
||||||
|
|
||||||
class AESEncryptedField(models.CharField):
|
class AESEncryptedField(models.CharField):
|
||||||
|
""" A Field that can be used in forms for adding the support
|
||||||
|
of AES ecnrypted fields """
|
||||||
def save_form_data(self, instance, data):
|
def save_form_data(self, instance, data):
|
||||||
setattr(instance, self.name,
|
setattr(instance, self.name,
|
||||||
binascii.b2a_base64(encrypt(settings.AES_KEY, data)))
|
binascii.b2a_base64(encrypt(settings.AES_KEY, data)))
|
||||||
|
@ -41,16 +78,10 @@ class AESEncryptedField(models.CharField):
|
||||||
return decrypt(settings.AES_KEY,
|
return decrypt(settings.AES_KEY,
|
||||||
binascii.a2b_base64(value)).decode('utf-8')
|
binascii.a2b_base64(value)).decode('utf-8')
|
||||||
|
|
||||||
def from_db_value(self, value, expression, connection, *args):
|
|
||||||
if value is None:
|
|
||||||
return value
|
|
||||||
return decrypt(settings.AES_KEY,
|
|
||||||
binascii.a2b_base64(value)).decode('utf-8')
|
|
||||||
|
|
||||||
def get_prep_value(self, value):
|
def get_prep_value(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return value
|
return value
|
||||||
return binascii.b2a_base64(encrypt(
|
return binascii.b2a_base64(encrypt(
|
||||||
settings.AES_KEY,
|
settings.AES_KEY,
|
||||||
value
|
value
|
||||||
))
|
))
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class PreferencesConfig(AppConfig):
|
|
||||||
name = 'preferences'
|
|
|
@ -44,12 +44,16 @@ class EditOptionalUserForm(ModelForm):
|
||||||
prefix=prefix,
|
prefix=prefix,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
self.fields['is_tel_mandatory'].label = 'Exiger un numéro de\
|
self.fields['is_tel_mandatory'].label = (
|
||||||
téléphone'
|
'Exiger un numéro de téléphone'
|
||||||
self.fields['user_solde'].label = 'Activation du solde pour\
|
)
|
||||||
les utilisateurs'
|
self.fields['user_solde'].label = (
|
||||||
|
'Activation du solde pour les utilisateurs'
|
||||||
|
)
|
||||||
self.fields['max_solde'].label = 'Solde maximum'
|
self.fields['max_solde'].label = 'Solde maximum'
|
||||||
self.fields['min_online_payment'].label = 'Montant de rechargement minimum en ligne'
|
self.fields['min_online_payment'].label = (
|
||||||
|
'Montant de rechargement minimum en ligne'
|
||||||
|
)
|
||||||
self.fields['self_adhesion'].label = 'Auto inscription'
|
self.fields['self_adhesion'].label = 'Auto inscription'
|
||||||
|
|
||||||
|
|
||||||
|
@ -162,7 +166,6 @@ class EditAssoOptionForm(ModelForm):
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class EditMailMessageOptionForm(ModelForm):
|
class EditMailMessageOptionForm(ModelForm):
|
||||||
"""Formulaire d'edition des messages de bienvenue personnalisés"""
|
"""Formulaire d'edition des messages de bienvenue personnalisés"""
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -27,26 +27,33 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.db import models
|
from django.db import models
|
||||||
import cotisations.models
|
from django.db.models.signals import post_save
|
||||||
import machines.models
|
|
||||||
from django.db.models.signals import post_save, post_delete
|
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
|
||||||
from .aes_field import AESEncryptedField
|
import cotisations.models
|
||||||
|
import machines.models
|
||||||
from re2o.mixins import AclMixin
|
from re2o.mixins import AclMixin
|
||||||
|
|
||||||
|
from .aes_field import AESEncryptedField
|
||||||
|
|
||||||
|
|
||||||
class PreferencesModel(models.Model):
|
class PreferencesModel(models.Model):
|
||||||
|
""" Base object for the Preferences objects
|
||||||
|
Defines methods to handle the cache of the settings (they should
|
||||||
|
not change a lot) """
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_in_cache(cls):
|
def set_in_cache(cls):
|
||||||
|
""" Save the preferences in a server-side cache """
|
||||||
instance, _created = cls.objects.get_or_create()
|
instance, _created = cls.objects.get_or_create()
|
||||||
cache.set(cls().__class__.__name__.lower(), instance, None)
|
cache.set(cls().__class__.__name__.lower(), instance, None)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_cached_value(cls, key):
|
def get_cached_value(cls, key):
|
||||||
|
""" Get the preferences from the server-side cache """
|
||||||
instance = cache.get(cls().__class__.__name__.lower())
|
instance = cache.get(cls().__class__.__name__.lower())
|
||||||
if instance == None:
|
if instance is None:
|
||||||
instance = cls.set_in_cache()
|
instance = cls.set_in_cache()
|
||||||
return getattr(instance, key)
|
return getattr(instance, key)
|
||||||
|
|
||||||
|
@ -111,7 +118,7 @@ class OptionalUser(AclMixin, PreferencesModel):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=OptionalUser)
|
@receiver(post_save, sender=OptionalUser)
|
||||||
def optionaluser_post_save(sender, **kwargs):
|
def optionaluser_post_save(**kwargs):
|
||||||
"""Ecriture dans le cache"""
|
"""Ecriture dans le cache"""
|
||||||
user_pref = kwargs['instance']
|
user_pref = kwargs['instance']
|
||||||
user_pref.set_in_cache()
|
user_pref.set_in_cache()
|
||||||
|
@ -146,7 +153,8 @@ class OptionalMachine(AclMixin, PreferencesModel):
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def ipv6(self):
|
def ipv6(self):
|
||||||
return not self.get_cached_value('ipv6_mode') == 'DISABLED'
|
""" Check if the IPv6 option is activated """
|
||||||
|
return not self.get_cached_value('ipv6_mode') == 'DISABLED'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
|
@ -155,7 +163,7 @@ class OptionalMachine(AclMixin, PreferencesModel):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=OptionalMachine)
|
@receiver(post_save, sender=OptionalMachine)
|
||||||
def optionalmachine_post_save(sender, **kwargs):
|
def optionalmachine_post_save(**kwargs):
|
||||||
"""Synchronisation ipv6 et ecriture dans le cache"""
|
"""Synchronisation ipv6 et ecriture dans le cache"""
|
||||||
machine_pref = kwargs['instance']
|
machine_pref = kwargs['instance']
|
||||||
machine_pref.set_in_cache()
|
machine_pref.set_in_cache()
|
||||||
|
@ -203,7 +211,7 @@ class OptionalTopologie(AclMixin, PreferencesModel):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=OptionalTopologie)
|
@receiver(post_save, sender=OptionalTopologie)
|
||||||
def optionaltopologie_post_save(sender, **kwargs):
|
def optionaltopologie_post_save(**kwargs):
|
||||||
"""Ecriture dans le cache"""
|
"""Ecriture dans le cache"""
|
||||||
topologie_pref = kwargs['instance']
|
topologie_pref = kwargs['instance']
|
||||||
topologie_pref.set_in_cache()
|
topologie_pref.set_in_cache()
|
||||||
|
@ -230,7 +238,7 @@ class GeneralOption(AclMixin, PreferencesModel):
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
GTU = models.FileField(
|
GTU = models.FileField(
|
||||||
upload_to = '',
|
upload_to='',
|
||||||
default="",
|
default="",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
@ -243,7 +251,7 @@ class GeneralOption(AclMixin, PreferencesModel):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=GeneralOption)
|
@receiver(post_save, sender=GeneralOption)
|
||||||
def generaloption_post_save(sender, **kwargs):
|
def generaloption_post_save(**kwargs):
|
||||||
"""Ecriture dans le cache"""
|
"""Ecriture dans le cache"""
|
||||||
general_pref = kwargs['instance']
|
general_pref = kwargs['instance']
|
||||||
general_pref.set_in_cache()
|
general_pref.set_in_cache()
|
||||||
|
@ -317,7 +325,7 @@ class AssoOption(AclMixin, PreferencesModel):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=AssoOption)
|
@receiver(post_save, sender=AssoOption)
|
||||||
def assooption_post_save(sender, **kwargs):
|
def assooption_post_save(**kwargs):
|
||||||
"""Ecriture dans le cache"""
|
"""Ecriture dans le cache"""
|
||||||
asso_pref = kwargs['instance']
|
asso_pref = kwargs['instance']
|
||||||
asso_pref.set_in_cache()
|
asso_pref.set_in_cache()
|
||||||
|
|
|
@ -1,3 +1,29 @@
|
||||||
from django.test import TestCase
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2017 Gabriel Détraz
|
||||||
|
# Copyright © 2017 Goulven Kermarec
|
||||||
|
# Copyright © 2017 Augustin Lemesle
|
||||||
|
# Copyright © 2018 Maël Kervella
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
"""preferences.tests
|
||||||
|
The tests for the Preferences module.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -27,8 +27,8 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
|
||||||
from . import views
|
|
||||||
import re2o
|
import re2o
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
@ -73,7 +73,7 @@ urlpatterns = [
|
||||||
r'^history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
r'^history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
||||||
re2o.views.history,
|
re2o.views.history,
|
||||||
name='history',
|
name='history',
|
||||||
kwargs={'application':'preferences'},
|
kwargs={'application': 'preferences'},
|
||||||
),
|
),
|
||||||
url(r'^$', views.display_options, name='display-options'),
|
url(r'^$', views.display_options, name='display-options'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -31,17 +31,17 @@ topologie, users, service...)
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import redirect
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.db.models import ProtectedError
|
from django.db.models import ProtectedError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
||||||
from reversion.models import Version
|
|
||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
|
|
||||||
from re2o.views import form
|
from re2o.views import form
|
||||||
from re2o.acl import can_create, can_edit, can_delete_set, can_view_all
|
from re2o.acl import can_create, can_edit, can_delete_set, can_view_all
|
||||||
|
|
||||||
from .forms import ServiceForm, DelServiceForm
|
from .forms import ServiceForm, DelServiceForm
|
||||||
from .models import Service, OptionalUser, OptionalMachine, AssoOption
|
from .models import Service, OptionalUser, OptionalMachine, AssoOption
|
||||||
from .models import MailMessageOption, GeneralOption, OptionalTopologie
|
from .models import MailMessageOption, GeneralOption, OptionalTopologie
|
||||||
|
@ -128,7 +128,7 @@ def add_service(request):
|
||||||
messages.success(request, "Ce service a été ajouté")
|
messages.success(request, "Ce service a été ajouté")
|
||||||
return redirect(reverse('preferences:display-options'))
|
return redirect(reverse('preferences:display-options'))
|
||||||
return form(
|
return form(
|
||||||
{'preferenceform': service, 'action_name' : 'Ajouter'},
|
{'preferenceform': service, 'action_name': 'Ajouter'},
|
||||||
'preferences/preferences.html',
|
'preferences/preferences.html',
|
||||||
request
|
request
|
||||||
)
|
)
|
||||||
|
@ -136,7 +136,7 @@ def add_service(request):
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Service)
|
@can_edit(Service)
|
||||||
def edit_service(request, service_instance, serviceid):
|
def edit_service(request, service_instance, **_kwargs):
|
||||||
"""Edition des services affichés sur la page d'accueil"""
|
"""Edition des services affichés sur la page d'accueil"""
|
||||||
service = ServiceForm(request.POST or None, instance=service_instance)
|
service = ServiceForm(request.POST or None, instance=service_instance)
|
||||||
if service.is_valid():
|
if service.is_valid():
|
||||||
|
@ -151,7 +151,7 @@ def edit_service(request, service_instance, serviceid):
|
||||||
messages.success(request, "Service modifié")
|
messages.success(request, "Service modifié")
|
||||||
return redirect(reverse('preferences:display-options'))
|
return redirect(reverse('preferences:display-options'))
|
||||||
return form(
|
return form(
|
||||||
{'preferenceform': service, 'action_name' : 'Editer'},
|
{'preferenceform': service, 'action_name': 'Editer'},
|
||||||
'preferences/preferences.html',
|
'preferences/preferences.html',
|
||||||
request
|
request
|
||||||
)
|
)
|
||||||
|
@ -175,7 +175,7 @@ def del_services(request, instances):
|
||||||
suivant %s ne peut être supprimé" % services_del)
|
suivant %s ne peut être supprimé" % services_del)
|
||||||
return redirect(reverse('preferences:display-options'))
|
return redirect(reverse('preferences:display-options'))
|
||||||
return form(
|
return form(
|
||||||
{'preferenceform': services, 'action_name' : 'Supprimer'},
|
{'preferenceform': services, 'action_name': 'Supprimer'},
|
||||||
'preferences/preferences.html',
|
'preferences/preferences.html',
|
||||||
request
|
request
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,4 +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
|
||||||
|
* ...
|
||||||
|
"""
|
||||||
|
|
141
re2o/acl.py
141
re2o/acl.py
|
@ -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,19 +163,25 @@ 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:
|
||||||
can, msg = instance.can_delete(request.user)
|
can, _msg = instance.can_delete(request.user)
|
||||||
if can:
|
if can:
|
||||||
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, "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
|
||||||
|
|
|
@ -1,3 +1,30 @@
|
||||||
#!/usr/bin/env python3
|
"""re2o.contributors
|
||||||
|
A list of the proud contributors to Re2o
|
||||||
|
"""
|
||||||
|
|
||||||
CONTRIBUTORS = ['Gabriel "Chirac" Détraz', 'Maël "MoaMoaK" Kervella', 'Hugo "Klafyvel" Levy--Falk', 'Augustin "Dahlaro" Lemesle', 'Goulven "Lhark" Kermarec', 'Guillaume "Guimoz" Goessel', 'Yoann "Nanoy" Pietri', 'Matthieu "Lebanni" Michelet', 'Arthur "Grizzly" Grisel-Davy', 'Simon "Rezatoune" Brélivet', 'Sellem Lev-Arcady', 'David "5-1" Sinquin', 'Pierre "Redstorm" Cadart', 'Éloi "Goslig" Alain', 'Laouen "Volgarr" Fernet', 'Joanne Steiner', '"Krokmou"', 'Thibault "Tipunchetrhum" de Boutray', 'Baptiste "B" Fournier', 'Daniel "Dstan" Stan', 'Hugo "Shaka" Hervieux', '"Mikachu"', 'Thomas "Nymous" Gaudin', '"Esum"']
|
CONTRIBUTORS = [
|
||||||
|
'Gabriel "Chirac" Détraz',
|
||||||
|
'Maël "MoaMoaK" Kervella',
|
||||||
|
'Hugo "Klafyvel" Levy--Falk',
|
||||||
|
'Augustin "Dahlaro" Lemesle',
|
||||||
|
'Goulven "Lhark" Kermarec',
|
||||||
|
'Guillaume "Guimoz" Goessel',
|
||||||
|
'Yoann "Nanoy" Pietri',
|
||||||
|
'Matthieu "Lebanni" Michelet',
|
||||||
|
'Arthur "Grizzly" Grisel-Davy',
|
||||||
|
'Simon "Rezatoune" Brélivet',
|
||||||
|
'Sellem Lev-Arcady',
|
||||||
|
'David "5-1" Sinquin',
|
||||||
|
'Pierre "Redstorm" Cadart',
|
||||||
|
'Éloi "Goslig" Alain',
|
||||||
|
'Laouen "Volgarr" Fernet',
|
||||||
|
'Joanne Steiner',
|
||||||
|
'"Krokmou"',
|
||||||
|
'Thibault "Tipunchetrhum" de Boutray',
|
||||||
|
'Baptiste "B" Fournier',
|
||||||
|
'Daniel "Dstan" Stan',
|
||||||
|
'Hugo "Shaka" Hervieux',
|
||||||
|
'"Mikachu"',
|
||||||
|
'Thomas "Nymous" Gaudin',
|
||||||
|
'"Esum"'
|
||||||
|
]
|
||||||
|
|
|
@ -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,21 +69,18 @@ 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:
|
||||||
result = user.has_perm(perm) # Don't supply 'obj', or else infinite recursion.
|
# Don't supply 'obj', or else infinite recursion.
|
||||||
|
result = user.has_perm(perm)
|
||||||
if result:
|
if result:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# If no requirement can be met, then permission is denied.
|
# If no requirement can be met, then permission is denied.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
class FieldPermissionModel(FieldPermissionModelMixin, models.Model):
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class FieldPermissionFormMixin:
|
class FieldPermissionFormMixin:
|
||||||
"""
|
"""
|
||||||
|
@ -71,9 +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
|
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -20,20 +20,28 @@
|
||||||
# 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.
|
||||||
"""
|
"""
|
||||||
Write in a python file the list of all contributors sorted by number of commits.
|
Write in a python file the list of all contributors sorted by number of
|
||||||
This list is extracted from the FedeRez 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):
|
||||||
help = 'Update contributors list'
|
""" The command object for `gen_contrib` """
|
||||||
|
help = 'Update contributors list'
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
contributeurs = [item.split('\t')[1] for item in os.popen("git shortlog -s -n").read().split("\n") if '\t' in item]
|
contributeurs = [
|
||||||
|
item.split('\t')[1]
|
||||||
|
for item in os.popen("git shortlog -s -n").read().split("\n")
|
||||||
|
if '\t' in item
|
||||||
|
]
|
||||||
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))
|
||||||
|
|
|
@ -19,27 +19,44 @@
|
||||||
# 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.get_comment() + ",%s" % ', '.join(field for field in self.changed_data))
|
reversion.set_comment(
|
||||||
|
reversion.get_comment() + ",%s"
|
||||||
|
% ', '.join(field for field in self.changed_data)
|
||||||
|
)
|
||||||
elif self.changed_data:
|
elif self.changed_data:
|
||||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in self.changed_data))
|
reversion.set_comment(
|
||||||
|
"Champs modifié(s) : %s"
|
||||||
|
% ', '.join(field for field in self.changed_data)
|
||||||
|
)
|
||||||
return super(FormRevMixin, self).save(*args, **kwargs)
|
return super(FormRevMixin, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,23 +64,29 @@ class AclMixin(object):
|
||||||
"""This mixin is used in nearly every class/models defined in re2o apps.
|
"""This mixin is used in nearly every class/models defined in re2o apps.
|
||||||
It is used by acl, in models (decorators can_...) and in templates tags
|
It is used by acl, in models (decorators can_...) and in templates tags
|
||||||
:get_instance: Applied on a class, take an id argument, return an instance
|
:get_instance: Applied on a class, take an id argument, return an instance
|
||||||
:can_create: Applied on a class, take the requested user, return if the user
|
:can_create: Applied on a class, take the requested user, return if the
|
||||||
can do the creation
|
user can do the creation
|
||||||
:can_edit: Applied on an instance, return if the user can edit the instance
|
:can_edit: Applied on an instance, return if the user can edit the
|
||||||
:can_delete: Applied on an instance, return if the user can delete the instance
|
instance
|
||||||
:can_view: Applied on an instance, return if the user can view the instance
|
:can_delete: Applied on an instance, return if the user can delete the
|
||||||
:can_view_all: Applied on a class, return if the user can view all instances"""
|
instance
|
||||||
|
:can_view: Applied on an instance, return if the user can view the
|
||||||
|
instance
|
||||||
|
:can_view_all: Applied on a class, return if the user can view all
|
||||||
|
instances"""
|
||||||
|
|
||||||
@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"""
|
||||||
|
@ -71,42 +94,66 @@ 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
|
||||||
:return: soit True, soit False avec la raison de l'échec"""
|
:return: soit True, soit False avec la raison de l'échec"""
|
||||||
return user_request.has_perm(cls.get_modulename() + '.add_' + cls.get_classname()), u"Vous n'avez pas le droit\
|
return (
|
||||||
de créer un " + cls.get_classname()
|
user_request.has_perm(
|
||||||
|
cls.get_modulename() + '.add_' + 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
|
||||||
:param user_request: Utilisateur qui fait la requête
|
:param user_request: Utilisateur qui fait la requête
|
||||||
:return: soit True, soit False avec la raison de l'échec"""
|
:return: soit True, soit False avec la raison de l'échec"""
|
||||||
return user_request.has_perm(self.get_modulename() + '.change_' + self.get_classname()), u"Vous n'avez pas le droit d'éditer des " + self.get_classname()
|
return (
|
||||||
|
user_request.has_perm(
|
||||||
|
self.get_modulename() + '.change_' + 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
|
||||||
:param user_request: Utilisateur qui fait la requête
|
:param user_request: Utilisateur qui fait la requête
|
||||||
:return: soit True, soit False avec la raison de l'échec"""
|
:return: soit True, soit False avec la raison de l'échec"""
|
||||||
return user_request.has_perm(self.get_modulename() + '.delete_' + self.get_classname()), u"Vous n'avez pas le droit d'éditer des " + self.get_classname()
|
return (
|
||||||
|
user_request.has_perm(
|
||||||
|
self.get_modulename() + '.delete_' + self.get_classname()
|
||||||
|
),
|
||||||
|
u"Vous n'avez pas le droit d'éditer des " + self.get_classname()
|
||||||
|
)
|
||||||
|
|
||||||
@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
|
||||||
:return: True ou False avec la raison de l'échec le cas échéant"""
|
:return: True ou False avec la raison de l'échec le cas échéant"""
|
||||||
return user_request.has_perm(cls.get_modulename() + '.view_' + cls.get_classname()), u"Vous n'avez pas le droit de voir des " + cls.get_classname()
|
return (
|
||||||
|
user_request.has_perm(
|
||||||
|
cls.get_modulename() + '.view_' + 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
|
||||||
:param user_request: instance user qui fait l'edition
|
:param user_request: instance user qui fait l'edition
|
||||||
:return: True ou False avec la raison de l'échec le cas échéant"""
|
:return: True ou False avec la raison de l'échec le cas échéant"""
|
||||||
return user_request.has_perm(self.get_modulename() + '.view_' + self.get_classname()), u"Vous n'avez pas le droit de voir des " + self.get_classname()
|
return (
|
||||||
|
user_request.has_perm(
|
||||||
|
self.get_modulename() + '.view_' + self.get_classname()
|
||||||
|
),
|
||||||
|
u"Vous n'avez pas le droit de voir des " + self.get_classname()
|
||||||
|
)
|
||||||
|
|
|
@ -18,33 +18,42 @@
|
||||||
# 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, sys, pwd
|
import os
|
||||||
|
from os.path import dirname
|
||||||
|
import sys
|
||||||
|
import pwd
|
||||||
|
|
||||||
|
from getpass import getpass
|
||||||
|
from reversion import revisions as reversion
|
||||||
|
|
||||||
proj_path="/var/www/re2o"
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE","re2o.settings")
|
|
||||||
sys.path.append(proj_path)
|
|
||||||
os.chdir(proj_path)
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
application = 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
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "re2o.settings")
|
||||||
from django.db import transaction
|
sys.path.append(proj_path)
|
||||||
from getpass import getpass
|
os.chdir(proj_path)
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
||||||
|
|
||||||
|
|
||||||
def get_user(pseudo):
|
def get_user(pseudo):
|
||||||
"""Cherche un utilisateur re2o à partir de son pseudo"""
|
"""Cherche un utilisateur re2o à partir de son pseudo"""
|
||||||
user = User.objects.filter(pseudo=pseudo)
|
user = User.objects.filter(pseudo=pseudo)
|
||||||
if len(user)==0:
|
if len(user) == 0:
|
||||||
raise CommandError("Utilisateur invalide")
|
raise CommandError("Utilisateur invalide")
|
||||||
if len(user)>1:
|
if len(user) > 1:
|
||||||
raise CommandError("Plusieurs utilisateurs correspondant à ce pseudo. Ceci NE DEVRAIT PAS arriver")
|
raise CommandError("Plusieurs utilisateurs correspondant à ce "
|
||||||
|
"pseudo. Ceci NE DEVRAIT PAS arriver")
|
||||||
return user[0]
|
return user[0]
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,7 +62,7 @@ def get_system_user():
|
||||||
return pwd.getpwuid(int(os.getenv("SUDO_UID") or os.getuid())).pw_name
|
return pwd.getpwuid(int(os.getenv("SUDO_UID") or os.getuid())).pw_name
|
||||||
|
|
||||||
|
|
||||||
def form_cli(Form,user,action,*args,**kwargs):
|
def form_cli(Form, user, action, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Remplit un formulaire à partir de la ligne de commande
|
Remplit un formulaire à partir de la ligne de commande
|
||||||
Form : le formulaire (sous forme de classe) à remplir
|
Form : le formulaire (sous forme de classe) à remplir
|
||||||
|
@ -61,26 +70,30 @@ def form_cli(Form,user,action,*args,**kwargs):
|
||||||
action : l'action réalisée par le formulaire (pour les logs)
|
action : l'action réalisée par le formulaire (pour les logs)
|
||||||
Les arguments suivants sont transmis tels quels au formulaire.
|
Les arguments suivants sont transmis tels quels au formulaire.
|
||||||
"""
|
"""
|
||||||
data={}
|
data = {}
|
||||||
dumb_form = Form(user=user,*args,**kwargs)
|
dumb_form = Form(user=user, *args, **kwargs)
|
||||||
for key in dumb_form.fields:
|
for key in dumb_form.fields:
|
||||||
if not dumb_form.fields[key].widget.input_type=='hidden':
|
if not dumb_form.fields[key].widget.input_type == 'hidden':
|
||||||
if dumb_form.fields[key].widget.input_type=='password':
|
if dumb_form.fields[key].widget.input_type == 'password':
|
||||||
data[key]=getpass("%s : " % dumb_form.fields[key].label)
|
data[key] = getpass("%s : " % dumb_form.fields[key].label)
|
||||||
else:
|
else:
|
||||||
data[key]=input("%s : " % dumb_form.fields[key].label)
|
data[key] = input("%s : " % dumb_form.fields[key].label)
|
||||||
|
|
||||||
form = Form(data,user=user,*args,**kwargs)
|
form = Form(data, user=user, *args, **kwargs)
|
||||||
if not form.is_valid():
|
if not form.is_valid():
|
||||||
sys.stderr.write("Erreurs : \n")
|
sys.stderr.write("Erreurs : \n")
|
||||||
for err in form.errors:
|
for err in form.errors:
|
||||||
#Oui, oui, on gère du HTML là où d'autres ont eu la lumineuse idée de le mettre
|
# Oui, oui, on gère du HTML là où d'autres ont eu la
|
||||||
sys.stderr.write("\t%s : %s\n" % (err,strip_tags(form.errors[err])))
|
# lumineuse idée de le mettre
|
||||||
|
sys.stderr.write(
|
||||||
|
"\t%s : %s\n" % (err, strip_tags(form.errors[err]))
|
||||||
|
)
|
||||||
raise CommandError("Formulaire invalide")
|
raise CommandError("Formulaire invalide")
|
||||||
|
|
||||||
with transaction.atomic(), reversion.create_revision():
|
with transaction.atomic(), reversion.create_revision():
|
||||||
form.save()
|
form.save()
|
||||||
reversion.set_user(user)
|
reversion.set_user(user)
|
||||||
reversion.set_comment(action)
|
reversion.set_comment(action)
|
||||||
|
|
||||||
sys.stdout.write("%s : effectué. La modification peut prendre quelques minutes pour s'appliquer.\n" % action)
|
sys.stdout.write("%s : effectué. La modification peut prendre "
|
||||||
|
"quelques minutes pour s'appliquer.\n" % action)
|
||||||
|
|
100
re2o/settings.py
100
re2o/settings.py
|
@ -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/' # to define 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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,30 +77,33 @@ 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',
|
||||||
'base_usergroup_dn' : 'ou=posix,ou=groups,dc=example,dc=org',
|
'base_usergroup_dn': 'ou=posix,ou=groups,dc=example,dc=org',
|
||||||
'base_userservicegroup_dn' : 'ou=services,ou=groups,dc=example,dc=org',
|
'base_userservicegroup_dn': 'ou=services,ou=groups,dc=example,dc=org',
|
||||||
'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 = ()
|
||||||
|
|
||||||
|
|
|
@ -18,4 +18,3 @@
|
||||||
# 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.
|
||||||
|
|
||||||
|
|
|
@ -85,32 +85,32 @@ register = template.Library()
|
||||||
|
|
||||||
MODEL_NAME = {
|
MODEL_NAME = {
|
||||||
# cotisations
|
# cotisations
|
||||||
'Facture' : cotisations.models.Facture,
|
'Facture': cotisations.models.Facture,
|
||||||
'Vente' : cotisations.models.Vente,
|
'Vente': cotisations.models.Vente,
|
||||||
'Article' : cotisations.models.Article,
|
'Article': cotisations.models.Article,
|
||||||
'Banque' : cotisations.models.Banque,
|
'Banque': cotisations.models.Banque,
|
||||||
'Paiement' : cotisations.models.Paiement,
|
'Paiement': cotisations.models.Paiement,
|
||||||
'Cotisation' : cotisations.models.Cotisation,
|
'Cotisation': cotisations.models.Cotisation,
|
||||||
# machines
|
# machines
|
||||||
'Machine' : machines.models.Machine,
|
'Machine': machines.models.Machine,
|
||||||
'MachineType' : machines.models.MachineType,
|
'MachineType': machines.models.MachineType,
|
||||||
'IpType' : machines.models.IpType,
|
'IpType': machines.models.IpType,
|
||||||
'Vlan' : machines.models.Vlan,
|
'Vlan': machines.models.Vlan,
|
||||||
'Nas' : machines.models.Nas,
|
'Nas': machines.models.Nas,
|
||||||
'SOA' : machines.models.SOA,
|
'SOA': machines.models.SOA,
|
||||||
'Extension' : machines.models.Extension,
|
'Extension': machines.models.Extension,
|
||||||
'Mx' : machines.models.Mx,
|
'Mx': machines.models.Mx,
|
||||||
'Ns' : machines.models.Ns,
|
'Ns': machines.models.Ns,
|
||||||
'Txt' : machines.models.Txt,
|
'Txt': machines.models.Txt,
|
||||||
'Srv' : machines.models.Srv,
|
'Srv': machines.models.Srv,
|
||||||
'Interface' : machines.models.Interface,
|
'Interface': machines.models.Interface,
|
||||||
'Domain' : machines.models.Domain,
|
'Domain': machines.models.Domain,
|
||||||
'IpList' : machines.models.IpList,
|
'IpList': machines.models.IpList,
|
||||||
'Ipv6List' : machines.models.Ipv6List,
|
'Ipv6List': machines.models.Ipv6List,
|
||||||
'machines.Service' : machines.models.Service,
|
'machines.Service': machines.models.Service,
|
||||||
'Service_link' : machines.models.Service_link,
|
'Service_link': machines.models.Service_link,
|
||||||
'OuverturePortList' : machines.models.OuverturePortList,
|
'OuverturePortList': machines.models.OuverturePortList,
|
||||||
'OuverturePort' : machines.models.OuverturePort,
|
'OuverturePort': machines.models.OuverturePort,
|
||||||
# preferences
|
# preferences
|
||||||
'OptionalUser': preferences.models.OptionalUser,
|
'OptionalUser': preferences.models.OptionalUser,
|
||||||
'OptionalMachine': preferences.models.OptionalMachine,
|
'OptionalMachine': preferences.models.OptionalMachine,
|
||||||
|
@ -120,25 +120,25 @@ MODEL_NAME = {
|
||||||
'AssoOption': preferences.models.AssoOption,
|
'AssoOption': preferences.models.AssoOption,
|
||||||
'MailMessageOption': preferences.models.MailMessageOption,
|
'MailMessageOption': preferences.models.MailMessageOption,
|
||||||
# topologie
|
# topologie
|
||||||
'Stack' : topologie.models.Stack,
|
'Stack': topologie.models.Stack,
|
||||||
'Switch' : topologie.models.Switch,
|
'Switch': topologie.models.Switch,
|
||||||
'AccessPoint' : topologie.models.AccessPoint,
|
'AccessPoint': topologie.models.AccessPoint,
|
||||||
'ModelSwitch' : topologie.models.ModelSwitch,
|
'ModelSwitch': topologie.models.ModelSwitch,
|
||||||
'ConstructorSwitch' : topologie.models.ConstructorSwitch,
|
'ConstructorSwitch': topologie.models.ConstructorSwitch,
|
||||||
'Port' : topologie.models.Port,
|
'Port': topologie.models.Port,
|
||||||
'Room' : topologie.models.Room,
|
'Room': topologie.models.Room,
|
||||||
'Building' : topologie.models.Building,
|
'Building': topologie.models.Building,
|
||||||
'SwitchBay' : topologie.models.SwitchBay,
|
'SwitchBay': topologie.models.SwitchBay,
|
||||||
# users
|
# users
|
||||||
'User' : users.models.User,
|
'User': users.models.User,
|
||||||
'Adherent' : users.models.Adherent,
|
'Adherent': users.models.Adherent,
|
||||||
'Club' : users.models.Club,
|
'Club': users.models.Club,
|
||||||
'ServiceUser' : users.models.ServiceUser,
|
'ServiceUser': users.models.ServiceUser,
|
||||||
'School' : users.models.School,
|
'School': users.models.School,
|
||||||
'ListRight' : users.models.ListRight,
|
'ListRight': users.models.ListRight,
|
||||||
'ListShell' : users.models.ListShell,
|
'ListShell': users.models.ListShell,
|
||||||
'Ban' : users.models.Ban,
|
'Ban': users.models.Ban,
|
||||||
'Whitelist' : users.models.Whitelist,
|
'Whitelist': users.models.Whitelist,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,17 +184,41 @@ def get_callback(tag_name, obj=None):
|
||||||
if tag_name == 'cannot_view_all':
|
if tag_name == 'cannot_view_all':
|
||||||
return acl_fct(obj.can_view_all, True)
|
return acl_fct(obj.can_view_all, True)
|
||||||
if tag_name == 'can_view_app':
|
if tag_name == 'can_view_app':
|
||||||
return acl_fct(lambda x : (not any(not sys.modules[o].can_view(x) for o in obj), None), False)
|
return acl_fct(
|
||||||
|
lambda x: (
|
||||||
|
not any(not sys.modules[o].can_view(x) for o in obj),
|
||||||
|
None
|
||||||
|
),
|
||||||
|
False
|
||||||
|
)
|
||||||
if tag_name == 'cannot_view_app':
|
if tag_name == 'cannot_view_app':
|
||||||
return acl_fct(lambda x : (not any(not sys.modules[o].can_view(x) for o in obj), None), True)
|
return acl_fct(
|
||||||
|
lambda x: (
|
||||||
|
not any(not sys.modules[o].can_view(x) for o in obj),
|
||||||
|
None
|
||||||
|
),
|
||||||
|
True
|
||||||
|
)
|
||||||
if tag_name == 'can_edit_history':
|
if tag_name == 'can_edit_history':
|
||||||
return acl_fct(lambda user:(user.has_perm('admin.change_logentry'),None),False)
|
return acl_fct(
|
||||||
|
lambda user: (user.has_perm('admin.change_logentry'), None),
|
||||||
|
False
|
||||||
|
)
|
||||||
if tag_name == 'cannot_edit_history':
|
if tag_name == 'cannot_edit_history':
|
||||||
return acl_fct(lambda user:(user.has_perm('admin.change_logentry'),None),True)
|
return acl_fct(
|
||||||
|
lambda user: (user.has_perm('admin.change_logentry'), None),
|
||||||
|
True
|
||||||
|
)
|
||||||
if tag_name == 'can_view_any_app':
|
if tag_name == 'can_view_any_app':
|
||||||
return acl_fct(lambda x : (any(sys.modules[o].can_view(x) for o in obj), None), False)
|
return acl_fct(
|
||||||
|
lambda x: (any(sys.modules[o].can_view(x) for o in obj), None),
|
||||||
|
False
|
||||||
|
)
|
||||||
if tag_name == 'cannot_view_any_app':
|
if tag_name == 'cannot_view_any_app':
|
||||||
return acl_fct(lambda x : (any(sys.modules[o].can_view(x) for o in obj), None), True)
|
return acl_fct(
|
||||||
|
lambda x: (any(sys.modules[o].can_view(x) for o in obj), None),
|
||||||
|
True
|
||||||
|
)
|
||||||
|
|
||||||
raise template.TemplateSyntaxError(
|
raise template.TemplateSyntaxError(
|
||||||
"%r tag is not a valid can_xxx tag" % tag_name
|
"%r tag is not a valid can_xxx tag" % tag_name
|
||||||
|
@ -246,11 +270,11 @@ def acl_app_filter(parser, token):
|
||||||
tag_name, *app_name = token.split_contents()
|
tag_name, *app_name = token.split_contents()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise template.TemplateSyntaxError(
|
raise template.TemplateSyntaxError(
|
||||||
"%r tag require 1 argument : an application"
|
"%r tag require 1 argument: an application"
|
||||||
% token.contents.split()[0]
|
% token.contents.split()[0]
|
||||||
)
|
)
|
||||||
for name in app_name:
|
for name in app_name:
|
||||||
if not name in sys.modules.keys():
|
if name not in sys.modules.keys():
|
||||||
raise template.TemplateSyntaxError(
|
raise template.TemplateSyntaxError(
|
||||||
"%r is not a registered application for acl."
|
"%r is not a registered application for acl."
|
||||||
% name
|
% name
|
||||||
|
@ -270,6 +294,7 @@ def acl_app_filter(parser, token):
|
||||||
|
|
||||||
return AclNode(callback, oknodes, konodes)
|
return AclNode(callback, oknodes, konodes)
|
||||||
|
|
||||||
|
|
||||||
@register.tag('can_change')
|
@register.tag('can_change')
|
||||||
@register.tag('cannot_change')
|
@register.tag('cannot_change')
|
||||||
def acl_change_filter(parser, token):
|
def acl_change_filter(parser, token):
|
||||||
|
@ -277,13 +302,13 @@ 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:]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise template.TemplateSyntaxError(
|
raise template.TemplateSyntaxError(
|
||||||
"%r tag require at least 2 argument : the model and the field"
|
"%r tag require at least 2 argument: the model and the field"
|
||||||
% token.contents.split()[0]
|
% token.contents.split()[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -306,6 +331,7 @@ def acl_change_filter(parser, token):
|
||||||
|
|
||||||
return AclNode(callback, oknodes, konodes, *args)
|
return AclNode(callback, oknodes, konodes, *args)
|
||||||
|
|
||||||
|
|
||||||
@register.tag('can_create')
|
@register.tag('can_create')
|
||||||
@register.tag('cannot_create')
|
@register.tag('cannot_create')
|
||||||
@register.tag('can_edit_all')
|
@register.tag('can_edit_all')
|
||||||
|
@ -324,7 +350,7 @@ def acl_model_filter(parser, token):
|
||||||
args = tag_content[2:]
|
args = tag_content[2:]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise template.TemplateSyntaxError(
|
raise template.TemplateSyntaxError(
|
||||||
"%r tag require at least 1 argument : the model"
|
"%r tag require at least 1 argument: the model"
|
||||||
% token.contents.split()[0]
|
% token.contents.split()[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -364,7 +390,7 @@ def acl_instance_filter(parser, token):
|
||||||
args = tag_content[2:]
|
args = tag_content[2:]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise template.TemplateSyntaxError(
|
raise template.TemplateSyntaxError(
|
||||||
"%r tag require at least 1 argument : the instance"
|
"%r tag require at least 1 argument: the instance"
|
||||||
% token.contents.split()[0]
|
% token.contents.split()[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -36,13 +36,14 @@ from bootstrap3.forms import render_field
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def massive_bootstrap_form(form, mbf_fields, *args, **kwargs):
|
def massive_bootstrap_form(form, mbf_fields, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Render a form where some specific fields are rendered using Twitter
|
Render a form where some specific fields are rendered using Twitter
|
||||||
Typeahead and/or splitree's Bootstrap Tokenfield to improve the performance, the
|
Typeahead and/or splitree's Bootstrap Tokenfield to improve the
|
||||||
speed and UX when dealing with very large datasets (select with 50k+ elts
|
performance, the speed and UX when dealing with very large datasets
|
||||||
for instance).
|
(select with 50k+ elts for instance).
|
||||||
When the fields specified should normally be rendered as a select with
|
When the fields specified should normally be rendered as a select with
|
||||||
single selectable option, Twitter Typeahead is used for a better display
|
single selectable option, Twitter Typeahead is used for a better display
|
||||||
and the matching query engine. When dealing with multiple selectable
|
and the matching query engine. When dealing with multiple selectable
|
||||||
|
@ -189,8 +190,6 @@ def massive_bootstrap_form(form, mbf_fields, *args, **kwargs):
|
||||||
return mbf_form.render()
|
return mbf_form.render()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MBFForm():
|
class MBFForm():
|
||||||
""" An object to hold all the information and useful methods needed to
|
""" An object to hold all the information and useful methods needed to
|
||||||
create and render a massive django form into an actual HTML and JS
|
create and render a massive django form into an actual HTML and JS
|
||||||
|
@ -198,7 +197,6 @@ class MBFForm():
|
||||||
Every field that is not listed is rendered as a normal bootstrap_field.
|
Every field that is not listed is rendered as a normal bootstrap_field.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, form, mbf_fields, *args, **kwargs):
|
def __init__(self, form, mbf_fields, *args, **kwargs):
|
||||||
# The django form object
|
# The django form object
|
||||||
self.form = form
|
self.form = form
|
||||||
|
@ -224,14 +222,13 @@ class MBFForm():
|
||||||
# HTML code to insert inside a template
|
# HTML code to insert inside a template
|
||||||
self.html = ""
|
self.html = ""
|
||||||
|
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
""" HTML code for the fully rendered form with all the necessary form
|
""" HTML code for the fully rendered form with all the necessary form
|
||||||
"""
|
"""
|
||||||
for name, field in self.form.fields.items():
|
for name, field in self.form.fields.items():
|
||||||
if not name in self.exclude:
|
if name not in self.exclude:
|
||||||
|
|
||||||
if name in self.fields and not name in self.hidden_fields:
|
if name in self.fields and name not in self.hidden_fields:
|
||||||
mbf_field = MBFField(
|
mbf_field = MBFField(
|
||||||
name,
|
name,
|
||||||
field,
|
field,
|
||||||
|
@ -256,9 +253,6 @@ class MBFForm():
|
||||||
return mark_safe(self.html)
|
return mark_safe(self.html)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MBFField():
|
class MBFField():
|
||||||
""" An object to hold all the information and useful methods needed to
|
""" An object to hold all the information and useful methods needed to
|
||||||
create and render a massive django form field into an actual HTML and JS
|
create and render a massive django form field into an actual HTML and JS
|
||||||
|
@ -270,7 +264,6 @@ class MBFField():
|
||||||
the displayed input. It's used to store the actual data that will be sent
|
the displayed input. It's used to store the actual data that will be sent
|
||||||
to the server """
|
to the server """
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, name_, field_, bound_, choices_, engine_, match_func_,
|
def __init__(self, name_, field_, bound_, choices_, engine_, match_func_,
|
||||||
update_on_, gen_select_, *args_, **kwargs_):
|
update_on_, gen_select_, *args_, **kwargs_):
|
||||||
|
|
||||||
|
@ -278,8 +271,8 @@ class MBFField():
|
||||||
if not isinstance(field_.widget, Select):
|
if not isinstance(field_.widget, Select):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
('Field named {f_name} is not a Select and'
|
('Field named {f_name} is not a Select and'
|
||||||
'can\'t be rendered with massive_bootstrap_form.'
|
'can\'t be rendered with massive_bootstrap_form.')
|
||||||
).format(
|
.format(
|
||||||
f_name=name_
|
f_name=name_
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -324,7 +317,6 @@ class MBFField():
|
||||||
self.args = args_
|
self.args = args_
|
||||||
self.kwargs = kwargs_
|
self.kwargs = kwargs_
|
||||||
|
|
||||||
|
|
||||||
def default_choices(self):
|
def default_choices(self):
|
||||||
""" JS code of the variable choices_<fieldname> """
|
""" JS code of the variable choices_<fieldname> """
|
||||||
|
|
||||||
|
@ -351,7 +343,6 @@ class MBFField():
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def default_engine(self):
|
def default_engine(self):
|
||||||
""" Default JS code of the variable engine_<field_name> """
|
""" Default JS code of the variable engine_<field_name> """
|
||||||
return (
|
return (
|
||||||
|
@ -365,7 +356,6 @@ class MBFField():
|
||||||
name=self.name
|
name=self.name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def default_datasets(self):
|
def default_datasets(self):
|
||||||
""" Default JS script of the datasets to use with typeahead """
|
""" Default JS script of the datasets to use with typeahead """
|
||||||
return (
|
return (
|
||||||
|
@ -384,7 +374,6 @@ class MBFField():
|
||||||
match_func=self.match_func
|
match_func=self.match_func
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def default_match_func(self):
|
def default_match_func(self):
|
||||||
""" Default JS code of the matching function to use with typeahed """
|
""" Default JS code of the matching function to use with typeahed """
|
||||||
return (
|
return (
|
||||||
|
@ -402,14 +391,12 @@ class MBFField():
|
||||||
name=self.name
|
name=self.name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
""" HTML code for the fully rendered field """
|
""" HTML code for the fully rendered field """
|
||||||
self.gen_displayed_div()
|
self.gen_displayed_div()
|
||||||
self.gen_hidden_div()
|
self.gen_hidden_div()
|
||||||
return mark_safe(self.html)
|
return mark_safe(self.html)
|
||||||
|
|
||||||
|
|
||||||
def gen_displayed_div(self):
|
def gen_displayed_div(self):
|
||||||
""" Generate HTML code for the div that contains displayed tags """
|
""" Generate HTML code for the div that contains displayed tags """
|
||||||
if self.gen_select:
|
if self.gen_select:
|
||||||
|
@ -434,7 +421,6 @@ class MBFField():
|
||||||
if not self.gen_select:
|
if not self.gen_select:
|
||||||
self.html += self.replace_input
|
self.html += self.replace_input
|
||||||
|
|
||||||
|
|
||||||
def gen_hidden_div(self):
|
def gen_hidden_div(self):
|
||||||
""" Generate HTML code for the div that contains hidden tags """
|
""" Generate HTML code for the div that contains hidden tags """
|
||||||
self.gen_full_js()
|
self.gen_full_js()
|
||||||
|
@ -449,7 +435,6 @@ class MBFField():
|
||||||
attrs={'id': self.div2_id}
|
attrs={'id': self.div2_id}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def hidden_input(self):
|
def hidden_input(self):
|
||||||
""" HTML for the hidden input element """
|
""" HTML for the hidden input element """
|
||||||
return render_tag(
|
return render_tag(
|
||||||
|
@ -462,14 +447,12 @@ class MBFField():
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def gen_full_js(self):
|
def gen_full_js(self):
|
||||||
""" Generate the full script tag containing the JS code """
|
""" Generate the full script tag containing the JS code """
|
||||||
self.create_js()
|
self.create_js()
|
||||||
self.fill_js()
|
self.fill_js()
|
||||||
self.get_script()
|
self.get_script()
|
||||||
|
|
||||||
|
|
||||||
def create_js(self):
|
def create_js(self):
|
||||||
""" Generate a template for the whole script to use depending on
|
""" Generate a template for the whole script to use depending on
|
||||||
gen_select and multiple """
|
gen_select and multiple """
|
||||||
|
@ -549,7 +532,6 @@ class MBFField():
|
||||||
'}} );'
|
'}} );'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def fill_js(self):
|
def fill_js(self):
|
||||||
""" Fill the template with the correct values """
|
""" Fill the template with the correct values """
|
||||||
self.js_script = self.js_script.format(
|
self.js_script = self.js_script.format(
|
||||||
|
@ -571,11 +553,12 @@ class MBFField():
|
||||||
typ_init_input=self.typeahead_init_input()
|
typ_init_input=self.typeahead_init_input()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_script(self):
|
def get_script(self):
|
||||||
""" Insert the JS code inside a script tag """
|
""" Insert the JS code inside a script tag """
|
||||||
self.js_script = render_tag('script', content=mark_safe(self.js_script))
|
self.js_script = render_tag(
|
||||||
|
'script',
|
||||||
|
content=mark_safe(self.js_script)
|
||||||
|
)
|
||||||
|
|
||||||
def del_select(self):
|
def del_select(self):
|
||||||
""" JS code to delete the select if it has been generated and replace
|
""" JS code to delete the select if it has been generated and replace
|
||||||
|
@ -589,7 +572,6 @@ class MBFField():
|
||||||
replace_input=self.replace_input
|
replace_input=self.replace_input
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def gen_hidden(self):
|
def gen_hidden(self):
|
||||||
""" JS code to add a hidden tag to store the value. """
|
""" JS code to add a hidden tag to store the value. """
|
||||||
return (
|
return (
|
||||||
|
@ -606,7 +588,6 @@ class MBFField():
|
||||||
html_name=self.bound.html_name
|
html_name=self.bound.html_name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def typeahead_init_input(self):
|
def typeahead_init_input(self):
|
||||||
""" JS code to init the fields values """
|
""" JS code to init the fields values """
|
||||||
init_key = self.bound.value() or '""'
|
init_key = self.bound.value() or '""'
|
||||||
|
@ -624,7 +605,6 @@ class MBFField():
|
||||||
hidden_id=self.hidden_id
|
hidden_id=self.hidden_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def typeahead_reset_input(self):
|
def typeahead_reset_input(self):
|
||||||
""" JS code to reset the fields values """
|
""" JS code to reset the fields values """
|
||||||
return (
|
return (
|
||||||
|
@ -635,7 +615,6 @@ class MBFField():
|
||||||
hidden_id=self.hidden_id
|
hidden_id=self.hidden_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def typeahead_select(self):
|
def typeahead_select(self):
|
||||||
""" JS code to create the function triggered when an item is selected
|
""" JS code to create the function triggered when an item is selected
|
||||||
through typeahead """
|
through typeahead """
|
||||||
|
@ -649,7 +628,6 @@ class MBFField():
|
||||||
hidden_id=self.hidden_id
|
hidden_id=self.hidden_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def typeahead_change(self):
|
def typeahead_change(self):
|
||||||
""" JS code of the function triggered when an item is changed (i.e.
|
""" JS code of the function triggered when an item is changed (i.e.
|
||||||
looses focus and value has changed since the moment it gained focus )
|
looses focus and value has changed since the moment it gained focus )
|
||||||
|
@ -666,7 +644,6 @@ class MBFField():
|
||||||
hidden_id=self.hidden_id
|
hidden_id=self.hidden_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def typeahead_updates(self):
|
def typeahead_updates(self):
|
||||||
""" JS code for binding external fields changes with a reset """
|
""" JS code for binding external fields changes with a reset """
|
||||||
reset_input = self.typeahead_reset_input()
|
reset_input = self.typeahead_reset_input()
|
||||||
|
@ -683,7 +660,6 @@ class MBFField():
|
||||||
) for u_id in self.update_on]
|
) for u_id in self.update_on]
|
||||||
return ''.join(updates)
|
return ''.join(updates)
|
||||||
|
|
||||||
|
|
||||||
def tokenfield_init_input(self):
|
def tokenfield_init_input(self):
|
||||||
""" JS code to init the fields values """
|
""" JS code to init the fields values """
|
||||||
init_key = self.bound.value() or '""'
|
init_key = self.bound.value() or '""'
|
||||||
|
@ -700,7 +676,6 @@ class MBFField():
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def tokenfield_reset_input(self):
|
def tokenfield_reset_input(self):
|
||||||
""" JS code to reset the fields values """
|
""" JS code to reset the fields values """
|
||||||
return (
|
return (
|
||||||
|
@ -709,7 +684,6 @@ class MBFField():
|
||||||
input_id=self.input_id
|
input_id=self.input_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def tokenfield_create(self):
|
def tokenfield_create(self):
|
||||||
""" JS code triggered when a new token is created in tokenfield. """
|
""" JS code triggered when a new token is created in tokenfield. """
|
||||||
return (
|
return (
|
||||||
|
@ -739,7 +713,6 @@ class MBFField():
|
||||||
div2_id=self.div2_id
|
div2_id=self.div2_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def tokenfield_edit(self):
|
def tokenfield_edit(self):
|
||||||
""" JS code triggered when a token is edited in tokenfield. """
|
""" JS code triggered when a token is edited in tokenfield. """
|
||||||
return (
|
return (
|
||||||
|
@ -765,7 +738,6 @@ class MBFField():
|
||||||
hidden_id=self.hidden_id
|
hidden_id=self.hidden_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def tokenfield_remove(self):
|
def tokenfield_remove(self):
|
||||||
""" JS code trigggered when a token is removed from tokenfield. """
|
""" JS code trigggered when a token is removed from tokenfield. """
|
||||||
return (
|
return (
|
||||||
|
@ -791,7 +763,6 @@ class MBFField():
|
||||||
hidden_id=self.hidden_id
|
hidden_id=self.hidden_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def tokenfield_updates(self):
|
def tokenfield_updates(self):
|
||||||
""" JS code for binding external fields changes with a reset """
|
""" JS code for binding external fields changes with a reset """
|
||||||
reset_input = self.tokenfield_reset_input()
|
reset_input = self.tokenfield_reset_input()
|
||||||
|
|
|
@ -19,12 +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
|
||||||
|
|
|
@ -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):
|
||||||
|
@ -118,14 +113,18 @@ def all_has_access(search_time=None):
|
||||||
|
|
||||||
def filter_active_interfaces(interface_set):
|
def filter_active_interfaces(interface_set):
|
||||||
"""Filtre les machines autorisées à sortir sur internet dans une requête"""
|
"""Filtre les machines autorisées à sortir sur internet dans une requête"""
|
||||||
return interface_set.filter(
|
return (interface_set
|
||||||
machine__in=Machine.objects.filter(
|
.filter(
|
||||||
user__in=all_has_access()
|
machine__in=Machine.objects.filter(
|
||||||
).filter(active=True)
|
user__in=all_has_access()
|
||||||
).select_related('domain').select_related('machine')\
|
).filter(active=True)
|
||||||
.select_related('type').select_related('ipv4')\
|
).select_related('domain')
|
||||||
.select_related('domain__extension').select_related('ipv4__ip_type')\
|
.select_related('machine')
|
||||||
.distinct()
|
.select_related('type')
|
||||||
|
.select_related('ipv4')
|
||||||
|
.select_related('domain__extension')
|
||||||
|
.select_related('ipv4__ip_type')
|
||||||
|
.distinct())
|
||||||
|
|
||||||
|
|
||||||
def filter_complete_interfaces(interface_set):
|
def filter_complete_interfaces(interface_set):
|
||||||
|
@ -160,6 +159,7 @@ def all_active_assigned_interfaces_count():
|
||||||
""" Version light seulement pour compter"""
|
""" Version light seulement pour compter"""
|
||||||
return all_active_interfaces_count().filter(ipv4__isnull=False)
|
return all_active_interfaces_count().filter(ipv4__isnull=False)
|
||||||
|
|
||||||
|
|
||||||
class SortTable:
|
class SortTable:
|
||||||
""" Class gathering uselful stuff to sort the colums of a table, according
|
""" 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
|
to the column and order requested. It's used with a dict of possible
|
||||||
|
@ -171,7 +171,8 @@ class SortTable:
|
||||||
# the url value and the values are a list of model field name to use to
|
# 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.
|
# 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
|
# A 'default' might be provided to specify what to do if the requested col
|
||||||
# doesn't match any keys.
|
# doesn't match any keys.
|
||||||
|
|
||||||
USERS_INDEX = {
|
USERS_INDEX = {
|
||||||
'user_name': ['name'],
|
'user_name': ['name'],
|
||||||
'user_surname': ['surname'],
|
'user_surname': ['surname'],
|
||||||
|
@ -255,7 +256,7 @@ class SortTable:
|
||||||
}
|
}
|
||||||
TOPOLOGIE_INDEX_MODEL_SWITCH = {
|
TOPOLOGIE_INDEX_MODEL_SWITCH = {
|
||||||
'model-switch_name': ['reference'],
|
'model-switch_name': ['reference'],
|
||||||
'model-switch_contructor' : ['constructor__name'],
|
'model-switch_contructor': ['constructor__name'],
|
||||||
'default': ['reference'],
|
'default': ['reference'],
|
||||||
}
|
}
|
||||||
TOPOLOGIE_INDEX_SWITCH_BAY = {
|
TOPOLOGIE_INDEX_SWITCH_BAY = {
|
||||||
|
@ -290,6 +291,7 @@ class SortTable:
|
||||||
else:
|
else:
|
||||||
return request
|
return request
|
||||||
|
|
||||||
|
|
||||||
def re2o_paginator(request, query_set, pagination_number):
|
def re2o_paginator(request, query_set, pagination_number):
|
||||||
"""Paginator script for list display in re2o.
|
"""Paginator script for list display in re2o.
|
||||||
:request:
|
:request:
|
||||||
|
@ -307,6 +309,7 @@ def re2o_paginator(request, query_set, pagination_number):
|
||||||
results = paginator.page(paginator.num_pages)
|
results = paginator.page(paginator.num_pages)
|
||||||
return results
|
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:
|
||||||
|
|
131
re2o/views.py
131
re2o/views.py
|
@ -26,30 +26,31 @@ 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 preferences
|
||||||
import os
|
from preferences.models import Service, GeneralOption, AssoOption
|
||||||
import time
|
import users
|
||||||
from itertools import chain
|
import cotisations
|
||||||
|
import topologie
|
||||||
from preferences.models import Service
|
import machines
|
||||||
from preferences.models import OptionalUser, GeneralOption, AssoOption
|
|
||||||
import users, preferences, cotisations, topologie, 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
|
||||||
|
|
||||||
|
|
||||||
def form(ctx, template, request):
|
def form(ctx, template, request):
|
||||||
"""Form générique, raccourci importé par les fonctions views du site"""
|
"""Form générique, raccourci importé par les fonctions views du site"""
|
||||||
context = ctx
|
context = ctx
|
||||||
|
@ -64,56 +65,58 @@ def index(request):
|
||||||
services[indice % 3].append(serv)
|
services[indice % 3].append(serv)
|
||||||
return form({'services_urls': services}, 're2o/index.html', request)
|
return form({'services_urls': services}, 're2o/index.html', request)
|
||||||
|
|
||||||
|
|
||||||
#: Binding the corresponding char sequence of history url to re2o models.
|
#: Binding the corresponding char sequence of history url to re2o models.
|
||||||
HISTORY_BIND = {
|
HISTORY_BIND = {
|
||||||
'users' : {
|
'users': {
|
||||||
'user' : users.models.User,
|
'user': users.models.User,
|
||||||
'ban' : users.models.Ban,
|
'ban': users.models.Ban,
|
||||||
'whitelist' : users.models.Whitelist,
|
'whitelist': users.models.Whitelist,
|
||||||
'school' : users.models.School,
|
'school': users.models.School,
|
||||||
'listright' : users.models.ListRight,
|
'listright': users.models.ListRight,
|
||||||
'serviceuser' : users.models.ServiceUser,
|
'serviceuser': users.models.ServiceUser,
|
||||||
'listshell' : users.models.ListShell,
|
'listshell': users.models.ListShell,
|
||||||
},
|
},
|
||||||
'preferences' : {
|
'preferences': {
|
||||||
'service' : preferences.models.Service,
|
'service': preferences.models.Service,
|
||||||
},
|
},
|
||||||
'cotisations' : {
|
'cotisations': {
|
||||||
'facture' : cotisations.models.Facture,
|
'facture': cotisations.models.Facture,
|
||||||
'article' : cotisations.models.Article,
|
'article': cotisations.models.Article,
|
||||||
'paiement' : cotisations.models.Paiement,
|
'paiement': cotisations.models.Paiement,
|
||||||
'banque' : cotisations.models.Banque,
|
'banque': cotisations.models.Banque,
|
||||||
},
|
},
|
||||||
'topologie' : {
|
'topologie': {
|
||||||
'switch' : topologie.models.Switch,
|
'switch': topologie.models.Switch,
|
||||||
'port' : topologie.models.Port,
|
'port': topologie.models.Port,
|
||||||
'room' : topologie.models.Room,
|
'room': topologie.models.Room,
|
||||||
'stack' : topologie.models.Stack,
|
'stack': topologie.models.Stack,
|
||||||
'modelswitch' : topologie.models.ModelSwitch,
|
'modelswitch': topologie.models.ModelSwitch,
|
||||||
'constructorswitch' : topologie.models.ConstructorSwitch,
|
'constructorswitch': topologie.models.ConstructorSwitch,
|
||||||
'accesspoint' : topologie.models.AccessPoint,
|
'accesspoint': topologie.models.AccessPoint,
|
||||||
'switchbay' : topologie.models.SwitchBay,
|
'switchbay': topologie.models.SwitchBay,
|
||||||
'building' : topologie.models.Building,
|
'building': topologie.models.Building,
|
||||||
},
|
},
|
||||||
'machines' : {
|
'machines': {
|
||||||
'machine' : machines.models.Machine,
|
'machine': machines.models.Machine,
|
||||||
'interface' : machines.models.Interface,
|
'interface': machines.models.Interface,
|
||||||
'domain' : machines.models.Domain,
|
'domain': machines.models.Domain,
|
||||||
'machinetype' : machines.models.MachineType,
|
'machinetype': machines.models.MachineType,
|
||||||
'iptype' : machines.models.IpType,
|
'iptype': machines.models.IpType,
|
||||||
'extension' : machines.models.Extension,
|
'extension': machines.models.Extension,
|
||||||
'soa' : machines.models.SOA,
|
'soa': machines.models.SOA,
|
||||||
'mx' : machines.models.Mx,
|
'mx': machines.models.Mx,
|
||||||
'txt' : machines.models.Txt,
|
'txt': machines.models.Txt,
|
||||||
'srv' : machines.models.Srv,
|
'srv': machines.models.Srv,
|
||||||
'ns' : machines.models.Ns,
|
'ns': machines.models.Ns,
|
||||||
'service' : machines.models.Service,
|
'service': machines.models.Service,
|
||||||
'vlan' : machines.models.Vlan,
|
'vlan': machines.models.Vlan,
|
||||||
'nas' : machines.models.Nas,
|
'nas': machines.models.Nas,
|
||||||
'ipv6list' : machines.models.Ipv6List,
|
'ipv6list': machines.models.Ipv6List,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def history(request, application, object_name, object_id):
|
def history(request, application, object_name, object_id):
|
||||||
"""Render history for a model.
|
"""Render history for a model.
|
||||||
|
@ -136,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}
|
||||||
|
@ -144,21 +147,23 @@ def history(request, application, object_name, object_id):
|
||||||
instance = model.get_instance(**kwargs)
|
instance = model.get_instance(**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(request, msg or "Vous ne pouvez pas accéder à ce menu")
|
messages.error(request, msg or "Vous ne pouvez pas accéder à ce menu")
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid':str(request.user.id)}
|
kwargs={'userid': str(request.user.id)}
|
||||||
))
|
))
|
||||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||||
reversions = Version.objects.get_for_object(instance)
|
reversions = Version.objects.get_for_object(instance)
|
||||||
if hasattr(instance, 'linked_objects'):
|
if hasattr(instance, 'linked_objects'):
|
||||||
for related_object in chain(instance.linked_objects()):
|
for related_object in chain(instance.linked_objects()):
|
||||||
reversions = reversions | Version.objects.get_for_object(related_object)
|
reversions = (reversions |
|
||||||
|
Version.objects.get_for_object(related_object))
|
||||||
reversions = re2o_paginator(request, reversions, pagination_number)
|
reversions = re2o_paginator(request, reversions, pagination_number)
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
|
@ -169,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()
|
||||||
|
@ -185,14 +193,14 @@ 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,
|
||||||
"re2o/about.html",
|
"re2o/about.html",
|
||||||
{
|
{
|
||||||
'description': option.description ,
|
'description': option.description,
|
||||||
'AssoName' : option.name ,
|
'AssoName': option.name,
|
||||||
'git_info_contributors': git_info_contributors,
|
'git_info_contributors': git_info_contributors,
|
||||||
'git_info_remote': git_info_remote,
|
'git_info_remote': git_info_remote,
|
||||||
'git_info_branch': git_info_branch,
|
'git_info_branch': git_info_branch,
|
||||||
|
@ -201,4 +209,3 @@ def about_page(request):
|
||||||
'dependencies': dependencies
|
'dependencies': dependencies
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,5 +20,8 @@
|
||||||
# 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.
|
||||||
|
"""search
|
||||||
|
The app in charge of evrything related to the search function
|
||||||
|
"""
|
||||||
|
|
||||||
from .acl import *
|
from .acl import *
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
Here are defined some functions to check acl on the application.
|
Here are defined some functions to check acl on the application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def can_view(user):
|
|
||||||
|
def can_view(_user):
|
||||||
"""Check if an user can view the application.
|
"""Check if an user can view the application.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
# 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.
|
||||||
|
"""search.tests
|
||||||
|
The tests for the Search module.
|
||||||
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
# from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -144,7 +144,7 @@ def search_single_word(word, filters, user,
|
||||||
if not User.can_view_all(user)[0]:
|
if not User.can_view_all(user)[0]:
|
||||||
filter_users &= Q(id=user.id)
|
filter_users &= Q(id=user.id)
|
||||||
filter_clubs = filter_users
|
filter_clubs = filter_users
|
||||||
filter_users |= Q(name__icontains=word)
|
filter_users |= Q(name__icontains=word)
|
||||||
filters['users'] |= filter_users
|
filters['users'] |= filter_users
|
||||||
filters['clubs'] |= filter_clubs
|
filters['clubs'] |= filter_clubs
|
||||||
|
|
||||||
|
|
|
@ -20,5 +20,9 @@
|
||||||
# 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.
|
||||||
|
"""topologie
|
||||||
|
The app in charge of handling all the informations about the network
|
||||||
|
topology like the switches, the rooms, how are the connections, ...
|
||||||
|
"""
|
||||||
|
|
||||||
from .acl import *
|
from .acl import *
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
Here are defined some functions to check acl on the application.
|
Here are defined some functions to check acl on the application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def can_view(user):
|
def can_view(user):
|
||||||
"""Check if an user can view the application.
|
"""Check if an user can view the application.
|
||||||
|
|
||||||
|
|
|
@ -32,16 +32,18 @@ NewSwitchForm)
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.forms import ModelForm
|
||||||
|
from django.db.models import Prefetch
|
||||||
|
|
||||||
from machines.models import Interface
|
from machines.models import Interface
|
||||||
from machines.forms import (
|
from machines.forms import (
|
||||||
EditInterfaceForm,
|
|
||||||
EditMachineForm,
|
EditMachineForm,
|
||||||
NewMachineForm
|
NewMachineForm
|
||||||
)
|
)
|
||||||
from django import forms
|
from re2o.mixins import FormRevMixin
|
||||||
from django.forms import ModelForm, Form
|
|
||||||
from django.db.models import Prefetch
|
from .models import (
|
||||||
from .models import (
|
|
||||||
Port,
|
Port,
|
||||||
Switch,
|
Switch,
|
||||||
Room,
|
Room,
|
||||||
|
@ -52,7 +54,7 @@ from .models import (
|
||||||
SwitchBay,
|
SwitchBay,
|
||||||
Building,
|
Building,
|
||||||
)
|
)
|
||||||
from re2o.mixins import FormRevMixin
|
|
||||||
|
|
||||||
class PortForm(FormRevMixin, ModelForm):
|
class PortForm(FormRevMixin, ModelForm):
|
||||||
"""Formulaire pour la création d'un port d'un switch
|
"""Formulaire pour la création d'un port d'un switch
|
||||||
|
@ -82,32 +84,48 @@ class EditPortForm(FormRevMixin, ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(EditPortForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(EditPortForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
self.fields['machine_interface'].queryset = Interface.objects.all()\
|
self.fields['machine_interface'].queryset = (
|
||||||
.select_related('domain__extension')
|
Interface.objects.all().select_related('domain__extension')
|
||||||
self.fields['related'].queryset = Port.objects.all()\
|
)
|
||||||
|
self.fields['related'].queryset = (
|
||||||
|
Port.objects.all()
|
||||||
.prefetch_related(Prefetch(
|
.prefetch_related(Prefetch(
|
||||||
'switch__interface_set',
|
'switch__interface_set',
|
||||||
queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension')
|
queryset=(Interface.objects
|
||||||
|
.select_related('ipv4__ip_type__extension')
|
||||||
|
.select_related('domain__extension'))
|
||||||
))
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AddPortForm(FormRevMixin, ModelForm):
|
class AddPortForm(FormRevMixin, ModelForm):
|
||||||
"""Permet d'ajouter un port de switch. Voir EditPortForm pour plus
|
"""Permet d'ajouter un port de switch. Voir EditPortForm pour plus
|
||||||
d'informations"""
|
d'informations"""
|
||||||
class Meta(PortForm.Meta):
|
class Meta(PortForm.Meta):
|
||||||
fields = ['port', 'room', 'machine_interface', 'related',
|
fields = [
|
||||||
'radius', 'vlan_force', 'details']
|
'port',
|
||||||
|
'room',
|
||||||
|
'machine_interface',
|
||||||
|
'related',
|
||||||
|
'radius',
|
||||||
|
'vlan_force',
|
||||||
|
'details'
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(AddPortForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(AddPortForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
self.fields['machine_interface'].queryset = Interface.objects.all()\
|
self.fields['machine_interface'].queryset = (
|
||||||
.select_related('domain__extension')
|
Interface.objects.all().select_related('domain__extension')
|
||||||
self.fields['related'].queryset = Port.objects.all()\
|
)
|
||||||
.prefetch_related(Prefetch(
|
self.fields['related'].queryset = (
|
||||||
'switch__interface_set',
|
Port.objects.all().prefetch_related(Prefetch(
|
||||||
queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension')
|
'switch__interface_set',
|
||||||
|
queryset=(Interface.objects
|
||||||
|
.select_related('ipv4__ip_type__extension')
|
||||||
|
.select_related('domain__extension'))
|
||||||
))
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class StackForm(FormRevMixin, ModelForm):
|
class StackForm(FormRevMixin, ModelForm):
|
||||||
|
@ -170,15 +188,22 @@ class CreatePortsForm(forms.Form):
|
||||||
|
|
||||||
class EditModelSwitchForm(FormRevMixin, ModelForm):
|
class EditModelSwitchForm(FormRevMixin, ModelForm):
|
||||||
"""Permet d'éediter un modèle de switch : nom et constructeur"""
|
"""Permet d'éediter un modèle de switch : nom et constructeur"""
|
||||||
members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False)
|
members = forms.ModelMultipleChoiceField(
|
||||||
|
Switch.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ModelSwitch
|
model = ModelSwitch
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(EditModelSwitchForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(EditModelSwitchForm, self).__init__(
|
||||||
|
*args,
|
||||||
|
prefix=prefix,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
instance = kwargs.get('instance', None)
|
instance = kwargs.get('instance', None)
|
||||||
if instance:
|
if instance:
|
||||||
self.initial['members'] = Switch.objects.filter(model=instance)
|
self.initial['members'] = Switch.objects.filter(model=instance)
|
||||||
|
@ -197,13 +222,20 @@ class EditConstructorSwitchForm(FormRevMixin, ModelForm):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(EditConstructorSwitchForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(EditConstructorSwitchForm, self).__init__(
|
||||||
|
*args,
|
||||||
|
prefix=prefix,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EditSwitchBayForm(FormRevMixin, ModelForm):
|
class EditSwitchBayForm(FormRevMixin, ModelForm):
|
||||||
"""Permet d'éditer une baie de brassage"""
|
"""Permet d'éditer une baie de brassage"""
|
||||||
members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False)
|
members = forms.ModelMultipleChoiceField(
|
||||||
|
Switch.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SwitchBay
|
model = SwitchBay
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
|
@ -47,9 +47,10 @@ from django.db import IntegrityError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
|
|
||||||
from machines.models import Machine, Interface, regen
|
from machines.models import Machine, regen
|
||||||
from re2o.mixins import AclMixin, RevMixin
|
from re2o.mixins import AclMixin, RevMixin
|
||||||
|
|
||||||
|
|
||||||
class Stack(AclMixin, RevMixin, models.Model):
|
class Stack(AclMixin, RevMixin, models.Model):
|
||||||
"""Un objet stack. Regrouppe des switchs en foreign key
|
"""Un objet stack. Regrouppe des switchs en foreign key
|
||||||
,contient une id de stack, un switch id min et max dans
|
,contient une id de stack, un switch id min et max dans
|
||||||
|
@ -85,12 +86,12 @@ class Stack(AclMixin, RevMixin, models.Model):
|
||||||
|
|
||||||
class AccessPoint(AclMixin, Machine):
|
class AccessPoint(AclMixin, Machine):
|
||||||
"""Define a wireless AP. Inherit from machines.interfaces
|
"""Define a wireless AP. Inherit from machines.interfaces
|
||||||
|
|
||||||
Definition pour une borne wifi , hérite de machines.interfaces
|
Definition pour une borne wifi , hérite de machines.interfaces
|
||||||
"""
|
"""
|
||||||
PRETTY_NAME = "Borne WiFi"
|
PRETTY_NAME = "Borne WiFi"
|
||||||
|
|
||||||
location = models.CharField(
|
location = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text="Détails sur la localisation de l'AP",
|
help_text="Détails sur la localisation de l'AP",
|
||||||
blank=True,
|
blank=True,
|
||||||
|
@ -120,7 +121,6 @@ class Switch(AclMixin, Machine):
|
||||||
id_max de la stack parente"""
|
id_max de la stack parente"""
|
||||||
PRETTY_NAME = "Switch / Commutateur"
|
PRETTY_NAME = "Switch / Commutateur"
|
||||||
|
|
||||||
|
|
||||||
number = models.PositiveIntegerField()
|
number = models.PositiveIntegerField()
|
||||||
stack = models.ForeignKey(
|
stack = models.ForeignKey(
|
||||||
'topologie.Stack',
|
'topologie.Stack',
|
||||||
|
@ -165,7 +165,8 @@ class Switch(AclMixin, Machine):
|
||||||
ne peut être nul"})
|
ne peut être nul"})
|
||||||
|
|
||||||
def create_ports(self, begin, end):
|
def create_ports(self, begin, end):
|
||||||
""" Crée les ports de begin à end si les valeurs données sont cohérentes. """
|
""" Crée les ports de begin à end si les valeurs données
|
||||||
|
sont cohérentes. """
|
||||||
|
|
||||||
s_begin = s_end = 0
|
s_begin = s_end = 0
|
||||||
nb_ports = self.ports.count()
|
nb_ports = self.ports.count()
|
||||||
|
@ -192,6 +193,7 @@ class Switch(AclMixin, Machine):
|
||||||
ValidationError("Création d'un port existant.")
|
ValidationError("Création d'un port existant.")
|
||||||
|
|
||||||
def main_interface(self):
|
def main_interface(self):
|
||||||
|
""" Returns the 'main' interface of the switch """
|
||||||
return self.interface_set.first()
|
return self.interface_set.first()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -331,14 +333,15 @@ class Port(AclMixin, RevMixin, models.Model):
|
||||||
("view_port", "Peut voir un objet port"),
|
("view_port", "Peut voir un objet port"),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_instance(portid, *args, **kwargs):
|
@classmethod
|
||||||
return Port.objects\
|
def get_instance(cls, portid, *_args, **kwargs):
|
||||||
.select_related('machine_interface__domain__extension')\
|
return (cls.objects
|
||||||
.select_related('machine_interface__machine__switch')\
|
.select_related('machine_interface__domain__extension')
|
||||||
.select_related('room')\
|
.select_related('machine_interface__machine__switch')
|
||||||
.select_related('related')\
|
.select_related('room')
|
||||||
.prefetch_related('switch__interface_set__domain__extension')\
|
.select_related('related')
|
||||||
.get(pk=portid)
|
.prefetch_related('switch__interface_set__domain__extension')
|
||||||
|
.get(pk=portid))
|
||||||
|
|
||||||
def make_port_related(self):
|
def make_port_related(self):
|
||||||
""" Synchronise le port distant sur self"""
|
""" Synchronise le port distant sur self"""
|
||||||
|
@ -363,18 +366,24 @@ class Port(AclMixin, RevMixin, models.Model):
|
||||||
cohérence"""
|
cohérence"""
|
||||||
if hasattr(self, 'switch'):
|
if hasattr(self, 'switch'):
|
||||||
if self.port > self.switch.number:
|
if self.port > self.switch.number:
|
||||||
raise ValidationError("Ce port ne peut exister,\
|
raise ValidationError(
|
||||||
numero trop élevé")
|
"Ce port ne peut exister, numero trop élevé"
|
||||||
if self.room and self.machine_interface or self.room and\
|
)
|
||||||
self.related or self.machine_interface and self.related:
|
if (self.room and self.machine_interface or
|
||||||
raise ValidationError("Chambre, interface et related_port sont\
|
self.room and self.related or
|
||||||
mutuellement exclusifs")
|
self.machine_interface and self.related):
|
||||||
|
raise ValidationError(
|
||||||
|
"Chambre, interface et related_port sont mutuellement "
|
||||||
|
"exclusifs"
|
||||||
|
)
|
||||||
if self.related == self:
|
if self.related == self:
|
||||||
raise ValidationError("On ne peut relier un port à lui même")
|
raise ValidationError("On ne peut relier un port à lui même")
|
||||||
if self.related and not self.related.related:
|
if self.related and not self.related.related:
|
||||||
if self.related.machine_interface or self.related.room:
|
if self.related.machine_interface or self.related.room:
|
||||||
raise ValidationError("Le port relié est déjà occupé, veuillez\
|
raise ValidationError(
|
||||||
le libérer avant de créer une relation")
|
"Le port relié est déjà occupé, veuillez le libérer "
|
||||||
|
"avant de créer une relation"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.make_port_related()
|
self.make_port_related()
|
||||||
elif hasattr(self, 'related_port'):
|
elif hasattr(self, 'related_port'):
|
||||||
|
@ -402,18 +411,18 @@ class Room(AclMixin, RevMixin, models.Model):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=AccessPoint)
|
@receiver(post_save, sender=AccessPoint)
|
||||||
def ap_post_save(sender, **kwargs):
|
def ap_post_save(**_kwargs):
|
||||||
"""Regeneration des noms des bornes vers le controleur"""
|
"""Regeneration des noms des bornes vers le controleur"""
|
||||||
regen('unifi-ap-names')
|
regen('unifi-ap-names')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=AccessPoint)
|
@receiver(post_delete, sender=AccessPoint)
|
||||||
def ap_post_delete(sender, **kwargs):
|
def ap_post_delete(**_kwargs):
|
||||||
"""Regeneration des noms des bornes vers le controleur"""
|
"""Regeneration des noms des bornes vers le controleur"""
|
||||||
regen('unifi-ap-names')
|
regen('unifi-ap-names')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Stack)
|
@receiver(post_delete, sender=Stack)
|
||||||
def stack_post_delete(sender, **kwargs):
|
def stack_post_delete(**_kwargs):
|
||||||
"""Vide les id des switches membres d'une stack supprimée"""
|
"""Vide les id des switches membres d'une stack supprimée"""
|
||||||
Switch.objects.filter(stack=None).update(stack_member_id=None)
|
Switch.objects.filter(stack=None).update(stack_member_id=None)
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
# 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.
|
||||||
|
"""topologie.tests
|
||||||
|
The tests for the Topologie module.
|
||||||
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
# from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
|
@ -51,12 +51,10 @@ urlpatterns = [
|
||||||
url(r'^switch/(?P<switchid>[0-9]+)$',
|
url(r'^switch/(?P<switchid>[0-9]+)$',
|
||||||
views.index_port,
|
views.index_port,
|
||||||
name='index-port'),
|
name='index-port'),
|
||||||
url(
|
url(r'^history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
||||||
r'^history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
|
||||||
re2o.views.history,
|
re2o.views.history,
|
||||||
name='history',
|
name='history',
|
||||||
kwargs={'application':'topologie'},
|
kwargs={'application': 'topologie'}),
|
||||||
),
|
|
||||||
url(r'^edit_port/(?P<portid>[0-9]+)$', views.edit_port, name='edit-port'),
|
url(r'^edit_port/(?P<portid>[0-9]+)$', views.edit_port, name='edit-port'),
|
||||||
url(r'^new_port/(?P<switchid>[0-9]+)$', views.new_port, name='new-port'),
|
url(r'^new_port/(?P<switchid>[0-9]+)$', views.new_port, name='new-port'),
|
||||||
url(r'^del_port/(?P<portid>[0-9]+)$', views.del_port, name='del-port'),
|
url(r'^del_port/(?P<portid>[0-9]+)$', views.del_port, name='del-port'),
|
||||||
|
@ -64,7 +62,9 @@ urlpatterns = [
|
||||||
views.edit_switch,
|
views.edit_switch,
|
||||||
name='edit-switch'),
|
name='edit-switch'),
|
||||||
url(r'^new_stack/$', views.new_stack, name='new-stack'),
|
url(r'^new_stack/$', views.new_stack, name='new-stack'),
|
||||||
url(r'^index_physical_grouping/$', views.index_physical_grouping, name='index-physical-grouping'),
|
url(r'^index_physical_grouping/$',
|
||||||
|
views.index_physical_grouping,
|
||||||
|
name='index-physical-grouping'),
|
||||||
url(r'^edit_stack/(?P<stackid>[0-9]+)$',
|
url(r'^edit_stack/(?P<stackid>[0-9]+)$',
|
||||||
views.edit_stack,
|
views.edit_stack,
|
||||||
name='edit-stack'),
|
name='edit-stack'),
|
||||||
|
@ -73,16 +73,13 @@ urlpatterns = [
|
||||||
name='del-stack'),
|
name='del-stack'),
|
||||||
url(r'^index_model_switch/$',
|
url(r'^index_model_switch/$',
|
||||||
views.index_model_switch,
|
views.index_model_switch,
|
||||||
name='index-model-switch'
|
name='index-model-switch'),
|
||||||
),
|
|
||||||
url(r'^index_model_switch/$',
|
url(r'^index_model_switch/$',
|
||||||
views.index_model_switch,
|
views.index_model_switch,
|
||||||
name='index-model-switch'
|
name='index-model-switch'),
|
||||||
),
|
|
||||||
url(r'^new_model_switch/$',
|
url(r'^new_model_switch/$',
|
||||||
views.new_model_switch,
|
views.new_model_switch,
|
||||||
name='new-model-switch'
|
name='new-model-switch'),
|
||||||
),
|
|
||||||
url(r'^edit_model_switch/(?P<modelswitchid>[0-9]+)$',
|
url(r'^edit_model_switch/(?P<modelswitchid>[0-9]+)$',
|
||||||
views.edit_model_switch,
|
views.edit_model_switch,
|
||||||
name='edit-model-switch'),
|
name='edit-model-switch'),
|
||||||
|
@ -91,8 +88,7 @@ urlpatterns = [
|
||||||
name='del-model-switch'),
|
name='del-model-switch'),
|
||||||
url(r'^new_constructor_switch/$',
|
url(r'^new_constructor_switch/$',
|
||||||
views.new_constructor_switch,
|
views.new_constructor_switch,
|
||||||
name='new-constructor-switch'
|
name='new-constructor-switch'),
|
||||||
),
|
|
||||||
url(r'^edit_constructor_switch/(?P<constructorswitchid>[0-9]+)$',
|
url(r'^edit_constructor_switch/(?P<constructorswitchid>[0-9]+)$',
|
||||||
views.edit_constructor_switch,
|
views.edit_constructor_switch,
|
||||||
name='edit-constructor-switch'),
|
name='edit-constructor-switch'),
|
||||||
|
@ -101,8 +97,7 @@ urlpatterns = [
|
||||||
name='del-constructor-switch'),
|
name='del-constructor-switch'),
|
||||||
url(r'^new_switch_bay/$',
|
url(r'^new_switch_bay/$',
|
||||||
views.new_switch_bay,
|
views.new_switch_bay,
|
||||||
name='new-switch-bay'
|
name='new-switch-bay'),
|
||||||
),
|
|
||||||
url(r'^edit_switch_bay/(?P<switchbayid>[0-9]+)$',
|
url(r'^edit_switch_bay/(?P<switchbayid>[0-9]+)$',
|
||||||
views.edit_switch_bay,
|
views.edit_switch_bay,
|
||||||
name='edit-switch-bay'),
|
name='edit-switch-bay'),
|
||||||
|
@ -111,8 +106,7 @@ urlpatterns = [
|
||||||
name='del-switch-bay'),
|
name='del-switch-bay'),
|
||||||
url(r'^new_building/$',
|
url(r'^new_building/$',
|
||||||
views.new_building,
|
views.new_building,
|
||||||
name='new-building'
|
name='new-building'),
|
||||||
),
|
|
||||||
url(r'^edit_building/(?P<buildingid>[0-9]+)$',
|
url(r'^edit_building/(?P<buildingid>[0-9]+)$',
|
||||||
views.edit_building,
|
views.edit_building,
|
||||||
name='edit-building'),
|
name='edit-building'),
|
||||||
|
|
|
@ -38,36 +38,11 @@ from __future__ import unicode_literals
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from django.db import transaction
|
|
||||||
from django.db.models import ProtectedError, Prefetch
|
from django.db.models import ProtectedError, Prefetch
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from topologie.models import (
|
|
||||||
Switch,
|
|
||||||
Port,
|
|
||||||
Room,
|
|
||||||
Stack,
|
|
||||||
ModelSwitch,
|
|
||||||
ConstructorSwitch,
|
|
||||||
AccessPoint,
|
|
||||||
SwitchBay,
|
|
||||||
Building
|
|
||||||
)
|
|
||||||
from topologie.forms import EditPortForm, NewSwitchForm, EditSwitchForm
|
|
||||||
from topologie.forms import (
|
|
||||||
AddPortForm,
|
|
||||||
EditRoomForm,
|
|
||||||
StackForm,
|
|
||||||
EditModelSwitchForm,
|
|
||||||
EditConstructorSwitchForm,
|
|
||||||
CreatePortsForm,
|
|
||||||
AddAccessPointForm,
|
|
||||||
EditAccessPointForm,
|
|
||||||
EditSwitchBayForm,
|
|
||||||
EditBuildingForm
|
|
||||||
)
|
|
||||||
from users.views import form
|
from users.views import form
|
||||||
from re2o.utils import re2o_paginator, SortTable
|
from re2o.utils import re2o_paginator, SortTable
|
||||||
from re2o.acl import (
|
from re2o.acl import (
|
||||||
|
@ -79,8 +54,6 @@ from re2o.acl import (
|
||||||
)
|
)
|
||||||
from machines.forms import (
|
from machines.forms import (
|
||||||
DomainForm,
|
DomainForm,
|
||||||
NewMachineForm,
|
|
||||||
EditMachineForm,
|
|
||||||
EditInterfaceForm,
|
EditInterfaceForm,
|
||||||
AddInterfaceForm
|
AddInterfaceForm
|
||||||
)
|
)
|
||||||
|
@ -88,17 +61,46 @@ from machines.views import generate_ipv4_mbf_param
|
||||||
from machines.models import Interface
|
from machines.models import Interface
|
||||||
from preferences.models import AssoOption, GeneralOption
|
from preferences.models import AssoOption, GeneralOption
|
||||||
|
|
||||||
|
from .models import (
|
||||||
|
Switch,
|
||||||
|
Port,
|
||||||
|
Room,
|
||||||
|
Stack,
|
||||||
|
ModelSwitch,
|
||||||
|
ConstructorSwitch,
|
||||||
|
AccessPoint,
|
||||||
|
SwitchBay,
|
||||||
|
Building
|
||||||
|
)
|
||||||
|
from .forms import (
|
||||||
|
EditPortForm,
|
||||||
|
NewSwitchForm,
|
||||||
|
EditSwitchForm,
|
||||||
|
AddPortForm,
|
||||||
|
EditRoomForm,
|
||||||
|
StackForm,
|
||||||
|
EditModelSwitchForm,
|
||||||
|
EditConstructorSwitchForm,
|
||||||
|
CreatePortsForm,
|
||||||
|
AddAccessPointForm,
|
||||||
|
EditAccessPointForm,
|
||||||
|
EditSwitchBayForm,
|
||||||
|
EditBuildingForm
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_view_all(Switch)
|
@can_view_all(Switch)
|
||||||
def index(request):
|
def index(request):
|
||||||
""" Vue d'affichage de tous les swicthes"""
|
""" Vue d'affichage de tous les swicthes"""
|
||||||
switch_list = Switch.objects\
|
switch_list = (Switch.objects
|
||||||
.prefetch_related(Prefetch(
|
.prefetch_related(Prefetch(
|
||||||
'interface_set',
|
'interface_set',
|
||||||
queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension')
|
queryset=(Interface.objects
|
||||||
))\
|
.select_related('ipv4__ip_type__extension')
|
||||||
.select_related('stack')
|
.select_related('domain__extension'))
|
||||||
|
))
|
||||||
|
.select_related('stack'))
|
||||||
switch_list = SortTable.sort(
|
switch_list = SortTable.sort(
|
||||||
switch_list,
|
switch_list,
|
||||||
request.GET.get('col'),
|
request.GET.get('col'),
|
||||||
|
@ -107,9 +109,11 @@ def index(request):
|
||||||
)
|
)
|
||||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||||
switch_list = re2o_paginator(request, switch_list, pagination_number)
|
switch_list = re2o_paginator(request, switch_list, pagination_number)
|
||||||
return render(request, 'topologie/index.html', {
|
return render(
|
||||||
'switch_list': switch_list
|
request,
|
||||||
})
|
'topologie/index.html',
|
||||||
|
{'switch_list': switch_list}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -117,27 +121,33 @@ def index(request):
|
||||||
@can_view(Switch)
|
@can_view(Switch)
|
||||||
def index_port(request, switch, switchid):
|
def index_port(request, switch, switchid):
|
||||||
""" Affichage de l'ensemble des ports reliés à un switch particulier"""
|
""" Affichage de l'ensemble des ports reliés à un switch particulier"""
|
||||||
port_list = Port.objects.filter(switch=switch)\
|
port_list = (Port.objects
|
||||||
.select_related('room')\
|
.filter(switch=switch)
|
||||||
.select_related('machine_interface__domain__extension')\
|
.select_related('room')
|
||||||
.select_related('machine_interface__machine__user')\
|
.select_related('machine_interface__domain__extension')
|
||||||
.select_related('related__switch')\
|
.select_related('machine_interface__machine__user')
|
||||||
.prefetch_related(Prefetch(
|
.select_related('related__switch')
|
||||||
'related__switch__interface_set',
|
.prefetch_related(Prefetch(
|
||||||
queryset=Interface.objects.select_related('domain__extension')
|
'related__switch__interface_set',
|
||||||
))\
|
queryset=(Interface.objects
|
||||||
.select_related('switch')
|
.select_related('domain__extension'))
|
||||||
|
))
|
||||||
|
.select_related('switch'))
|
||||||
port_list = SortTable.sort(
|
port_list = SortTable.sort(
|
||||||
port_list,
|
port_list,
|
||||||
request.GET.get('col'),
|
request.GET.get('col'),
|
||||||
request.GET.get('order'),
|
request.GET.get('order'),
|
||||||
SortTable.TOPOLOGIE_INDEX_PORT
|
SortTable.TOPOLOGIE_INDEX_PORT
|
||||||
)
|
)
|
||||||
return render(request, 'topologie/index_p.html', {
|
return render(
|
||||||
'port_list': port_list,
|
request,
|
||||||
'id_switch': switchid,
|
'topologie/index_p.html',
|
||||||
'nom_switch': switch
|
{
|
||||||
})
|
'port_list': port_list,
|
||||||
|
'id_switch': switchid,
|
||||||
|
'nom_switch': switch
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -153,20 +163,24 @@ def index_room(request):
|
||||||
)
|
)
|
||||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||||
room_list = re2o_paginator(request, room_list, pagination_number)
|
room_list = re2o_paginator(request, room_list, pagination_number)
|
||||||
return render(request, 'topologie/index_room.html', {
|
return render(
|
||||||
'room_list': room_list
|
request,
|
||||||
})
|
'topologie/index_room.html',
|
||||||
|
{'room_list': room_list}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_view_all(AccessPoint)
|
@can_view_all(AccessPoint)
|
||||||
def index_ap(request):
|
def index_ap(request):
|
||||||
""" Affichage de l'ensemble des bornes"""
|
""" Affichage de l'ensemble des bornes"""
|
||||||
ap_list = AccessPoint.objects\
|
ap_list = (AccessPoint.objects
|
||||||
.prefetch_related(Prefetch(
|
.prefetch_related(Prefetch(
|
||||||
'interface_set',
|
'interface_set',
|
||||||
queryset=Interface.objects.select_related('ipv4__ip_type__extension').select_related('domain__extension')
|
queryset=(Interface.objects
|
||||||
))
|
.select_related('ipv4__ip_type__extension')
|
||||||
|
.select_related('domain__extension'))
|
||||||
|
)))
|
||||||
ap_list = SortTable.sort(
|
ap_list = SortTable.sort(
|
||||||
ap_list,
|
ap_list,
|
||||||
request.GET.get('col'),
|
request.GET.get('col'),
|
||||||
|
@ -175,9 +189,11 @@ def index_ap(request):
|
||||||
)
|
)
|
||||||
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
pagination_number = GeneralOption.get_cached_value('pagination_number')
|
||||||
ap_list = re2o_paginator(request, ap_list, pagination_number)
|
ap_list = re2o_paginator(request, ap_list, pagination_number)
|
||||||
return render(request, 'topologie/index_ap.html', {
|
return render(
|
||||||
'ap_list': ap_list
|
request,
|
||||||
})
|
'topologie/index_ap.html',
|
||||||
|
{'ap_list': ap_list}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -186,8 +202,10 @@ def index_ap(request):
|
||||||
@can_view_all(SwitchBay)
|
@can_view_all(SwitchBay)
|
||||||
def index_physical_grouping(request):
|
def index_physical_grouping(request):
|
||||||
"""Affichage de la liste des stacks (affiche l'ensemble des switches)"""
|
"""Affichage de la liste des stacks (affiche l'ensemble des switches)"""
|
||||||
stack_list = Stack.objects\
|
stack_list = (Stack.objects
|
||||||
.prefetch_related('switch_set__interface_set__domain__extension')
|
.prefetch_related(
|
||||||
|
'switch_set__interface_set__domain__extension'
|
||||||
|
))
|
||||||
building_list = Building.objects.all()
|
building_list = Building.objects.all()
|
||||||
switch_bay_list = SwitchBay.objects.select_related('building')
|
switch_bay_list = SwitchBay.objects.select_related('building')
|
||||||
stack_list = SortTable.sort(
|
stack_list = SortTable.sort(
|
||||||
|
@ -208,11 +226,15 @@ def index_physical_grouping(request):
|
||||||
request.GET.get('order'),
|
request.GET.get('order'),
|
||||||
SortTable.TOPOLOGIE_INDEX_SWITCH_BAY
|
SortTable.TOPOLOGIE_INDEX_SWITCH_BAY
|
||||||
)
|
)
|
||||||
return render(request, 'topologie/index_physical_grouping.html', {
|
return render(
|
||||||
'stack_list': stack_list,
|
request,
|
||||||
'switch_bay_list': switch_bay_list,
|
'topologie/index_physical_grouping.html',
|
||||||
'building_list' : building_list,
|
{
|
||||||
})
|
'stack_list': stack_list,
|
||||||
|
'switch_bay_list': switch_bay_list,
|
||||||
|
'building_list': building_list,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -234,10 +256,14 @@ def index_model_switch(request):
|
||||||
request.GET.get('order'),
|
request.GET.get('order'),
|
||||||
SortTable.TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH
|
SortTable.TOPOLOGIE_INDEX_CONSTRUCTOR_SWITCH
|
||||||
)
|
)
|
||||||
return render(request, 'topologie/index_model_switch.html', {
|
return render(
|
||||||
'model_switch_list': model_switch_list,
|
request,
|
||||||
'constructor_switch_list': constructor_switch_list,
|
'topologie/index_model_switch.html',
|
||||||
})
|
{
|
||||||
|
'model_switch_list': model_switch_list,
|
||||||
|
'constructor_switch_list': constructor_switch_list,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -260,14 +286,17 @@ def new_port(request, switchid):
|
||||||
messages.error(request, "Ce port existe déjà")
|
messages.error(request, "Ce port existe déjà")
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'topologie:index-port',
|
'topologie:index-port',
|
||||||
kwargs={'switchid':switchid}
|
kwargs={'switchid': switchid}
|
||||||
))
|
))
|
||||||
return form({'id_switch': switchid,'topoform': port, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'id_switch': switchid, 'topoform': port, 'action_name': 'Ajouter'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Port)
|
@can_edit(Port)
|
||||||
def edit_port(request, port_object, portid):
|
def edit_port(request, port_object, **_kwargs):
|
||||||
""" Edition d'un port. Permet de changer le switch parent et
|
""" Edition d'un port. Permet de changer le switch parent et
|
||||||
l'affectation du port"""
|
l'affectation du port"""
|
||||||
|
|
||||||
|
@ -279,25 +308,36 @@ def edit_port(request, port_object, portid):
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'topologie:index-port',
|
'topologie:index-port',
|
||||||
kwargs={'switchid': str(port_object.switch.id)}
|
kwargs={'switchid': str(port_object.switch.id)}
|
||||||
))
|
))
|
||||||
return form({'id_switch': str(port_object.switch.id), 'topoform': port, 'action_name' : 'Editer'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{
|
||||||
|
'id_switch': str(port_object.switch.id),
|
||||||
|
'topoform': port,
|
||||||
|
'action_name': 'Editer'
|
||||||
|
},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_delete(Port)
|
@can_delete(Port)
|
||||||
def del_port(request, port, portid):
|
def del_port(request, port, **_kwargs):
|
||||||
""" Supprime le port"""
|
""" Supprime le port"""
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
try:
|
try:
|
||||||
port.delete()
|
port.delete()
|
||||||
messages.success(request, "Le port a été détruit")
|
messages.success(request, "Le port a été détruit")
|
||||||
except ProtectedError:
|
except ProtectedError:
|
||||||
messages.error(request, "Le port %s est affecté à un autre objet,\
|
messages.error(
|
||||||
impossible de le supprimer" % port)
|
request,
|
||||||
|
("Le port %s est affecté à un autre objet, impossible "
|
||||||
|
"de le supprimer" % port)
|
||||||
|
)
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'topologie:index-port',
|
'topologie:index-port',
|
||||||
kwargs={'switchid':str(port.switch.id)}
|
kwargs={'switchid': str(port.switch.id)}
|
||||||
))
|
))
|
||||||
return form({'objet': port}, 'topologie/delete.html', request)
|
return form({'objet': port}, 'topologie/delete.html', request)
|
||||||
|
|
||||||
|
|
||||||
|
@ -309,39 +349,50 @@ def new_stack(request):
|
||||||
if stack.is_valid():
|
if stack.is_valid():
|
||||||
stack.save()
|
stack.save()
|
||||||
messages.success(request, "Stack crée")
|
messages.success(request, "Stack crée")
|
||||||
return form({'topoform': stack, 'action_name' : 'Créer'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': stack, 'action_name': 'Créer'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Stack)
|
@can_edit(Stack)
|
||||||
def edit_stack(request, stack, stackid):
|
def edit_stack(request, stack, **_kwargs):
|
||||||
"""Edition d'un stack (nombre de switches, nom...)"""
|
"""Edition d'un stack (nombre de switches, nom...)"""
|
||||||
stack = StackForm(request.POST or None, instance=stack)
|
stack = StackForm(request.POST or None, instance=stack)
|
||||||
if stack.is_valid():
|
if stack.is_valid():
|
||||||
if stack.changed_data:
|
if stack.changed_data:
|
||||||
stack.save()
|
stack.save()
|
||||||
return redirect(reverse('topologie:index-physical-grouping'))
|
return redirect(reverse('topologie:index-physical-grouping'))
|
||||||
return form({'topoform': stack, 'action_name' : 'Editer'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': stack, 'action_name': 'Editer'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_delete(Stack)
|
@can_delete(Stack)
|
||||||
def del_stack(request, stack, stackid):
|
def del_stack(request, stack, **_kwargs):
|
||||||
"""Supprime un stack"""
|
"""Supprime un stack"""
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
try:
|
try:
|
||||||
stack.delete()
|
stack.delete()
|
||||||
messages.success(request, "La stack a eté détruite")
|
messages.success(request, "La stack a eté détruite")
|
||||||
except ProtectedError:
|
except ProtectedError:
|
||||||
messages.error(request, "La stack %s est affectée à un autre\
|
messages.error(
|
||||||
objet, impossible de la supprimer" % stack)
|
request,
|
||||||
|
("La stack %s est affectée à un autre objet, impossible "
|
||||||
|
"de la supprimer" % stack)
|
||||||
|
)
|
||||||
return redirect(reverse('topologie:index-physical-grouping'))
|
return redirect(reverse('topologie:index-physical-grouping'))
|
||||||
return form({'objet': stack}, 'topologie/delete.html', request)
|
return form({'objet': stack}, 'topologie/delete.html', request)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Stack)
|
@can_edit(Stack)
|
||||||
def edit_switchs_stack(request, stack, stackid):
|
def edit_switchs_stack(request, stack, **_kwargs):
|
||||||
"""Permet d'éditer la liste des switches dans une stack et l'ajouter"""
|
"""Permet d'éditer la liste des switches dans une stack et l'ajouter"""
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
@ -372,30 +423,37 @@ def new_switch(request):
|
||||||
if switch.is_valid() and interface.is_valid():
|
if switch.is_valid() and interface.is_valid():
|
||||||
user = AssoOption.get_cached_value('utilisateur_asso')
|
user = AssoOption.get_cached_value('utilisateur_asso')
|
||||||
if not user:
|
if not user:
|
||||||
messages.error(request, "L'user association n'existe pas encore,\
|
messages.error(
|
||||||
veuillez le créer ou le linker dans preferences")
|
request,
|
||||||
|
("L'user association n'existe pas encore, veuillez le "
|
||||||
|
"créer ou le linker dans preferences")
|
||||||
|
)
|
||||||
return redirect(reverse('topologie:index'))
|
return redirect(reverse('topologie:index'))
|
||||||
new_switch = switch.save(commit=False)
|
new_switch_obj = switch.save(commit=False)
|
||||||
new_switch.user = user
|
new_switch_obj.user = user
|
||||||
new_interface_instance = interface.save(commit=False)
|
new_interface_obj = interface.save(commit=False)
|
||||||
domain.instance.interface_parent = new_interface_instance
|
domain.instance.interface_parent = new_interface_obj
|
||||||
if domain.is_valid():
|
if domain.is_valid():
|
||||||
new_domain_instance = domain.save(commit=False)
|
new_domain_obj = domain.save(commit=False)
|
||||||
new_switch.save()
|
new_switch_obj.save()
|
||||||
new_interface_instance.machine = new_switch
|
new_interface_obj.machine = new_switch_obj
|
||||||
new_interface_instance.save()
|
new_interface_obj.save()
|
||||||
new_domain_instance.interface_parent = new_interface_instance
|
new_domain_obj.interface_parent = new_interface_obj
|
||||||
new_domain_instance.save()
|
new_domain_obj.save()
|
||||||
messages.success(request, "Le switch a été créé")
|
messages.success(request, "Le switch a été créé")
|
||||||
return redirect(reverse('topologie:index'))
|
return redirect(reverse('topologie:index'))
|
||||||
i_mbf_param = generate_ipv4_mbf_param(interface, False)
|
i_mbf_param = generate_ipv4_mbf_param(interface, False)
|
||||||
return form({
|
return form(
|
||||||
'topoform': interface,
|
{
|
||||||
'machineform': switch,
|
'topoform': interface,
|
||||||
'domainform': domain,
|
'machineform': switch,
|
||||||
'i_mbf_param': i_mbf_param,
|
'domainform': domain,
|
||||||
'device' : 'switch',
|
'i_mbf_param': i_mbf_param,
|
||||||
}, 'topologie/topo_more.html', request)
|
'device': 'switch',
|
||||||
|
},
|
||||||
|
'topologie/topo_more.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -430,9 +488,13 @@ def create_ports(request, switchid):
|
||||||
messages.error(request, ''.join(e))
|
messages.error(request, ''.join(e))
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'topologie:index-port',
|
'topologie:index-port',
|
||||||
kwargs={'switchid':switchid}
|
kwargs={'switchid': switchid}
|
||||||
))
|
))
|
||||||
return form({'id_switch': switchid, 'topoform': port_form}, 'topologie/switch.html', request)
|
return form(
|
||||||
|
{'id_switch': switchid, 'topoform': port_form},
|
||||||
|
'topologie/switch.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -456,26 +518,30 @@ def edit_switch(request, switch, switchid):
|
||||||
instance=switch.interface_set.first().domain
|
instance=switch.interface_set.first().domain
|
||||||
)
|
)
|
||||||
if switch_form.is_valid() and interface_form.is_valid():
|
if switch_form.is_valid() and interface_form.is_valid():
|
||||||
new_switch = switch_form.save(commit=False)
|
new_switch_obj = switch_form.save(commit=False)
|
||||||
new_interface_instance = interface_form.save(commit=False)
|
new_interface_obj = interface_form.save(commit=False)
|
||||||
new_domain = domain_form.save(commit=False)
|
new_domain_obj = domain_form.save(commit=False)
|
||||||
if switch_form.changed_data:
|
if switch_form.changed_data:
|
||||||
new_switch.save()
|
new_switch_obj.save()
|
||||||
if interface_form.changed_data:
|
if interface_form.changed_data:
|
||||||
new_interface_instance.save()
|
new_interface_obj.save()
|
||||||
if domain_form.changed_data:
|
if domain_form.changed_data:
|
||||||
new_domain.save()
|
new_domain_obj.save()
|
||||||
messages.success(request, "Le switch a bien été modifié")
|
messages.success(request, "Le switch a bien été modifié")
|
||||||
return redirect(reverse('topologie:index'))
|
return redirect(reverse('topologie:index'))
|
||||||
i_mbf_param = generate_ipv4_mbf_param(interface_form, False )
|
i_mbf_param = generate_ipv4_mbf_param(interface_form, False)
|
||||||
return form({
|
return form(
|
||||||
'id_switch': switchid,
|
{
|
||||||
'topoform': interface_form,
|
'id_switch': switchid,
|
||||||
'machineform': switch_form,
|
'topoform': interface_form,
|
||||||
'domainform': domain_form,
|
'machineform': switch_form,
|
||||||
'i_mbf_param': i_mbf_param,
|
'domainform': domain_form,
|
||||||
'device' : 'switch',
|
'i_mbf_param': i_mbf_param,
|
||||||
}, 'topologie/topo_more.html', request)
|
'device': 'switch',
|
||||||
|
},
|
||||||
|
'topologie/topo_more.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -498,35 +564,42 @@ def new_ap(request):
|
||||||
if ap.is_valid() and interface.is_valid():
|
if ap.is_valid() and interface.is_valid():
|
||||||
user = AssoOption.get_cached_value('utilisateur_asso')
|
user = AssoOption.get_cached_value('utilisateur_asso')
|
||||||
if not user:
|
if not user:
|
||||||
messages.error(request, "L'user association n'existe pas encore,\
|
messages.error(
|
||||||
veuillez le créer ou le linker dans preferences")
|
request,
|
||||||
|
("L'user association n'existe pas encore, veuillez le "
|
||||||
|
"créer ou le linker dans preferences")
|
||||||
|
)
|
||||||
return redirect(reverse('topologie:index'))
|
return redirect(reverse('topologie:index'))
|
||||||
new_ap = ap.save(commit=False)
|
new_ap_obj = ap.save(commit=False)
|
||||||
new_ap.user = user
|
new_ap_obj.user = user
|
||||||
new_interface = interface.save(commit=False)
|
new_interface_obj = interface.save(commit=False)
|
||||||
domain.instance.interface_parent = new_interface
|
domain.instance.interface_parent = new_interface_obj
|
||||||
if domain.is_valid():
|
if domain.is_valid():
|
||||||
new_domain_instance = domain.save(commit=False)
|
new_domain_obj = domain.save(commit=False)
|
||||||
new_ap.save()
|
new_ap_obj.save()
|
||||||
new_interface.machine = new_ap
|
new_interface_obj.machine = new_ap_obj
|
||||||
new_interface.save()
|
new_interface_obj.save()
|
||||||
new_domain_instance.interface_parent = new_interface
|
new_domain_obj.interface_parent = new_interface_obj
|
||||||
new_domain_instance.save()
|
new_domain_obj.save()
|
||||||
messages.success(request, "La borne a été créé")
|
messages.success(request, "La borne a été créé")
|
||||||
return redirect(reverse('topologie:index-ap'))
|
return redirect(reverse('topologie:index-ap'))
|
||||||
i_mbf_param = generate_ipv4_mbf_param(interface, False)
|
i_mbf_param = generate_ipv4_mbf_param(interface, False)
|
||||||
return form({
|
return form(
|
||||||
'topoform': interface,
|
{
|
||||||
'machineform': ap,
|
'topoform': interface,
|
||||||
'domainform': domain,
|
'machineform': ap,
|
||||||
'i_mbf_param': i_mbf_param,
|
'domainform': domain,
|
||||||
'device' : 'wifi ap',
|
'i_mbf_param': i_mbf_param,
|
||||||
}, 'topologie/topo_more.html', request)
|
'device': 'wifi ap',
|
||||||
|
},
|
||||||
|
'topologie/topo_more.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(AccessPoint)
|
@can_edit(AccessPoint)
|
||||||
def edit_ap(request, ap, accesspointid):
|
def edit_ap(request, ap, **_kwargs):
|
||||||
""" Edition d'un switch. Permet de chambre nombre de ports,
|
""" Edition d'un switch. Permet de chambre nombre de ports,
|
||||||
place dans le stack, interface et machine associée"""
|
place dans le stack, interface et machine associée"""
|
||||||
interface_form = EditInterfaceForm(
|
interface_form = EditInterfaceForm(
|
||||||
|
@ -546,29 +619,36 @@ def edit_ap(request, ap, accesspointid):
|
||||||
if ap_form.is_valid() and interface_form.is_valid():
|
if ap_form.is_valid() and interface_form.is_valid():
|
||||||
user = AssoOption.get_cached_value('utilisateur_asso')
|
user = AssoOption.get_cached_value('utilisateur_asso')
|
||||||
if not user:
|
if not user:
|
||||||
messages.error(request, "L'user association n'existe pas encore,\
|
messages.error(
|
||||||
veuillez le créer ou le linker dans preferences")
|
request,
|
||||||
|
("L'user association n'existe pas encore, veuillez le "
|
||||||
|
"créer ou le linker dans preferences")
|
||||||
|
)
|
||||||
return redirect(reverse('topologie:index-ap'))
|
return redirect(reverse('topologie:index-ap'))
|
||||||
new_ap = ap_form.save(commit=False)
|
new_ap_obj = ap_form.save(commit=False)
|
||||||
new_interface = interface_form.save(commit=False)
|
new_interface_obj = interface_form.save(commit=False)
|
||||||
new_domain = domain_form.save(commit=False)
|
new_domain_obj = domain_form.save(commit=False)
|
||||||
if ap_form.changed_data:
|
if ap_form.changed_data:
|
||||||
new_ap.save()
|
new_ap_obj.save()
|
||||||
if interface_form.changed_data:
|
if interface_form.changed_data:
|
||||||
new_interface.save()
|
new_interface_obj.save()
|
||||||
if domain_form.changed_data:
|
if domain_form.changed_data:
|
||||||
new_domain.save()
|
new_domain_obj.save()
|
||||||
messages.success(request, "La borne a été modifiée")
|
messages.success(request, "La borne a été modifiée")
|
||||||
return redirect(reverse('topologie:index-ap'))
|
return redirect(reverse('topologie:index-ap'))
|
||||||
i_mbf_param = generate_ipv4_mbf_param(interface_form, False )
|
i_mbf_param = generate_ipv4_mbf_param(interface_form, False)
|
||||||
return form({
|
return form(
|
||||||
'topoform': interface_form,
|
{
|
||||||
'machineform': ap_form,
|
'topoform': interface_form,
|
||||||
'domainform': domain_form,
|
'machineform': ap_form,
|
||||||
'i_mbf_param': i_mbf_param,
|
'domainform': domain_form,
|
||||||
'device' : 'wifi ap',
|
'i_mbf_param': i_mbf_param,
|
||||||
}, 'topologie/topo_more.html', request)
|
'device': 'wifi ap',
|
||||||
|
},
|
||||||
|
'topologie/topo_more.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_create(Room)
|
@can_create(Room)
|
||||||
|
@ -579,12 +659,16 @@ def new_room(request):
|
||||||
room.save()
|
room.save()
|
||||||
messages.success(request, "La chambre a été créé")
|
messages.success(request, "La chambre a été créé")
|
||||||
return redirect(reverse('topologie:index-room'))
|
return redirect(reverse('topologie:index-room'))
|
||||||
return form({'topoform': room, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': room, 'action_name': 'Ajouter'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Room)
|
@can_edit(Room)
|
||||||
def edit_room(request, room, roomid):
|
def edit_room(request, room, **_kwargs):
|
||||||
""" Edition numero et details de la chambre"""
|
""" Edition numero et details de la chambre"""
|
||||||
room = EditRoomForm(request.POST or None, instance=room)
|
room = EditRoomForm(request.POST or None, instance=room)
|
||||||
if room.is_valid():
|
if room.is_valid():
|
||||||
|
@ -592,25 +676,33 @@ def edit_room(request, room, roomid):
|
||||||
room.save()
|
room.save()
|
||||||
messages.success(request, "La chambre a bien été modifiée")
|
messages.success(request, "La chambre a bien été modifiée")
|
||||||
return redirect(reverse('topologie:index-room'))
|
return redirect(reverse('topologie:index-room'))
|
||||||
return form({'topoform': room, 'action_name' : 'Editer'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': room, 'action_name': 'Editer'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_delete(Room)
|
@can_delete(Room)
|
||||||
def del_room(request, room, roomid):
|
def del_room(request, room, **_kwargs):
|
||||||
""" Suppression d'un chambre"""
|
""" Suppression d'un chambre"""
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
try:
|
try:
|
||||||
room.delete()
|
room.delete()
|
||||||
messages.success(request, "La chambre/prise a été détruite")
|
messages.success(request, "La chambre/prise a été détruite")
|
||||||
except ProtectedError:
|
except ProtectedError:
|
||||||
messages.error(request, "La chambre %s est affectée à un autre objet,\
|
messages.error(
|
||||||
impossible de la supprimer (switch ou user)" % room)
|
request,
|
||||||
|
("La chambre %s est affectée à un autre objet, impossible "
|
||||||
|
"de la supprimer (switch ou user)" % room)
|
||||||
|
)
|
||||||
return redirect(reverse('topologie:index-room'))
|
return redirect(reverse('topologie:index-room'))
|
||||||
return form({
|
return form(
|
||||||
'objet': room,
|
{'objet': room, 'objet_name': 'Chambre'},
|
||||||
'objet_name': 'Chambre'
|
'topologie/delete.html',
|
||||||
}, 'topologie/delete.html', request)
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -622,39 +714,54 @@ def new_model_switch(request):
|
||||||
model_switch.save()
|
model_switch.save()
|
||||||
messages.success(request, "Le modèle a été créé")
|
messages.success(request, "Le modèle a été créé")
|
||||||
return redirect(reverse('topologie:index-model-switch'))
|
return redirect(reverse('topologie:index-model-switch'))
|
||||||
return form({'topoform': model_switch, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': model_switch, 'action_name': 'Ajouter'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(ModelSwitch)
|
@can_edit(ModelSwitch)
|
||||||
def edit_model_switch(request, model_switch, modelswitchid):
|
def edit_model_switch(request, model_switch, **_kwargs):
|
||||||
""" Edition d'un modèle de switch"""
|
""" Edition d'un modèle de switch"""
|
||||||
|
|
||||||
model_switch = EditModelSwitchForm(request.POST or None, instance=model_switch)
|
model_switch = EditModelSwitchForm(
|
||||||
|
request.POST or None,
|
||||||
|
instance=model_switch
|
||||||
|
)
|
||||||
if model_switch.is_valid():
|
if model_switch.is_valid():
|
||||||
if model_switch.changed_data:
|
if model_switch.changed_data:
|
||||||
model_switch.save()
|
model_switch.save()
|
||||||
messages.success(request, "Le modèle a bien été modifié")
|
messages.success(request, "Le modèle a bien été modifié")
|
||||||
return redirect(reverse('topologie:index-model-switch'))
|
return redirect(reverse('topologie:index-model-switch'))
|
||||||
return form({'topoform': model_switch, 'action_name' : 'Editer'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': model_switch, 'action_name': 'Editer'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_delete(ModelSwitch)
|
@can_delete(ModelSwitch)
|
||||||
def del_model_switch(request, model_switch, modelswitchid):
|
def del_model_switch(request, model_switch, **_kwargs):
|
||||||
""" Suppression d'un modèle de switch"""
|
""" Suppression d'un modèle de switch"""
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
try:
|
try:
|
||||||
model_switch.delete()
|
model_switch.delete()
|
||||||
messages.success(request, "Le modèle a été détruit")
|
messages.success(request, "Le modèle a été détruit")
|
||||||
except ProtectedError:
|
except ProtectedError:
|
||||||
messages.error(request, "Le modèle %s est affectée à un autre objet,\
|
messages.error(
|
||||||
impossible de la supprimer (switch ou user)" % model_switch)
|
request,
|
||||||
|
("Le modèle %s est affectée à un autre objet, impossible "
|
||||||
|
"de la supprimer (switch ou user)" % model_switch)
|
||||||
|
)
|
||||||
return redirect(reverse('topologie:index-model-switch'))
|
return redirect(reverse('topologie:index-model-switch'))
|
||||||
return form({
|
return form(
|
||||||
'objet': model_switch,
|
{'objet': model_switch, 'objet_name': 'Modèle de switch'},
|
||||||
'objet_name': 'Modèle de switch'
|
'topologie/delete.html',
|
||||||
}, 'topologie/delete.html', request)
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -666,12 +773,16 @@ def new_switch_bay(request):
|
||||||
switch_bay.save()
|
switch_bay.save()
|
||||||
messages.success(request, "La baie a été créé")
|
messages.success(request, "La baie a été créé")
|
||||||
return redirect(reverse('topologie:index-physical-grouping'))
|
return redirect(reverse('topologie:index-physical-grouping'))
|
||||||
return form({'topoform': switch_bay, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': switch_bay, 'action_name': 'Ajouter'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(SwitchBay)
|
@can_edit(SwitchBay)
|
||||||
def edit_switch_bay(request, switch_bay, switchbayid):
|
def edit_switch_bay(request, switch_bay, **_kwargs):
|
||||||
""" Edition d'une baie de switch"""
|
""" Edition d'une baie de switch"""
|
||||||
switch_bay = EditSwitchBayForm(request.POST or None, instance=switch_bay)
|
switch_bay = EditSwitchBayForm(request.POST or None, instance=switch_bay)
|
||||||
if switch_bay.is_valid():
|
if switch_bay.is_valid():
|
||||||
|
@ -679,25 +790,33 @@ def edit_switch_bay(request, switch_bay, switchbayid):
|
||||||
switch_bay.save()
|
switch_bay.save()
|
||||||
messages.success(request, "Le switch a bien été modifié")
|
messages.success(request, "Le switch a bien été modifié")
|
||||||
return redirect(reverse('topologie:index-physical-grouping'))
|
return redirect(reverse('topologie:index-physical-grouping'))
|
||||||
return form({'topoform': switch_bay, 'action_name' : 'Editer'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': switch_bay, 'action_name': 'Editer'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_delete(SwitchBay)
|
@can_delete(SwitchBay)
|
||||||
def del_switch_bay(request, switch_bay, switchbayid):
|
def del_switch_bay(request, switch_bay, **_kwargs):
|
||||||
""" Suppression d'une baie de switch"""
|
""" Suppression d'une baie de switch"""
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
try:
|
try:
|
||||||
switch_bay.delete()
|
switch_bay.delete()
|
||||||
messages.success(request, "La baie a été détruite")
|
messages.success(request, "La baie a été détruite")
|
||||||
except ProtectedError:
|
except ProtectedError:
|
||||||
messages.error(request, "La baie %s est affecté à un autre objet,\
|
messages.error(
|
||||||
impossible de la supprimer (switch ou user)" % switch_bay)
|
request,
|
||||||
|
("La baie %s est affecté à un autre objet, impossible "
|
||||||
|
"de la supprimer (switch ou user)" % switch_bay)
|
||||||
|
)
|
||||||
return redirect(reverse('topologie:index-physical-grouping'))
|
return redirect(reverse('topologie:index-physical-grouping'))
|
||||||
return form({
|
return form(
|
||||||
'objet': switch_bay,
|
{'objet': switch_bay, 'objet_name': 'Baie de switch'},
|
||||||
'objet_name': 'Baie de switch'
|
'topologie/delete.html',
|
||||||
}, 'topologie/delete.html', request)
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -709,12 +828,16 @@ def new_building(request):
|
||||||
building.save()
|
building.save()
|
||||||
messages.success(request, "Le batiment a été créé")
|
messages.success(request, "Le batiment a été créé")
|
||||||
return redirect(reverse('topologie:index-physical-grouping'))
|
return redirect(reverse('topologie:index-physical-grouping'))
|
||||||
return form({'topoform': building, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': building, 'action_name': 'Ajouter'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Building)
|
@can_edit(Building)
|
||||||
def edit_building(request, building, buildingid):
|
def edit_building(request, building, **_kwargs):
|
||||||
""" Edition d'un batiment"""
|
""" Edition d'un batiment"""
|
||||||
building = EditBuildingForm(request.POST or None, instance=building)
|
building = EditBuildingForm(request.POST or None, instance=building)
|
||||||
if building.is_valid():
|
if building.is_valid():
|
||||||
|
@ -722,25 +845,33 @@ def edit_building(request, building, buildingid):
|
||||||
building.save()
|
building.save()
|
||||||
messages.success(request, "Le batiment a bien été modifié")
|
messages.success(request, "Le batiment a bien été modifié")
|
||||||
return redirect(reverse('topologie:index-physical-grouping'))
|
return redirect(reverse('topologie:index-physical-grouping'))
|
||||||
return form({'topoform': building, 'action_name' : 'Editer'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': building, 'action_name': 'Editer'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_delete(Building)
|
@can_delete(Building)
|
||||||
def del_building(request, building, buildingid):
|
def del_building(request, building, **_kwargs):
|
||||||
""" Suppression d'un batiment"""
|
""" Suppression d'un batiment"""
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
try:
|
try:
|
||||||
building.delete()
|
building.delete()
|
||||||
messages.success(request, "La batiment a été détruit")
|
messages.success(request, "La batiment a été détruit")
|
||||||
except ProtectedError:
|
except ProtectedError:
|
||||||
messages.error(request, "Le batiment %s est affecté à un autre objet,\
|
messages.error(
|
||||||
impossible de la supprimer (switch ou user)" % building)
|
request,
|
||||||
|
("Le batiment %s est affecté à un autre objet, impossible "
|
||||||
|
"de la supprimer (switch ou user)" % building)
|
||||||
|
)
|
||||||
return redirect(reverse('topologie:index-physical-grouping'))
|
return redirect(reverse('topologie:index-physical-grouping'))
|
||||||
return form({
|
return form(
|
||||||
'objet': building,
|
{'objet': building, 'objet_name': 'Bâtiment'},
|
||||||
'objet_name': 'Bâtiment'
|
'topologie/delete.html',
|
||||||
}, 'topologie/delete.html', request)
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -752,36 +883,51 @@ def new_constructor_switch(request):
|
||||||
constructor_switch.save()
|
constructor_switch.save()
|
||||||
messages.success(request, "Le constructeur a été créé")
|
messages.success(request, "Le constructeur a été créé")
|
||||||
return redirect(reverse('topologie:index-model-switch'))
|
return redirect(reverse('topologie:index-model-switch'))
|
||||||
return form({'topoform': constructor_switch, 'action_name' : 'Ajouter'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': constructor_switch, 'action_name': 'Ajouter'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(ConstructorSwitch)
|
@can_edit(ConstructorSwitch)
|
||||||
def edit_constructor_switch(request, constructor_switch, constructorswitchid):
|
def edit_constructor_switch(request, constructor_switch, **_kwargs):
|
||||||
""" Edition d'un constructeur de switch"""
|
""" Edition d'un constructeur de switch"""
|
||||||
|
|
||||||
constructor_switch = EditConstructorSwitchForm(request.POST or None, instance=constructor_switch)
|
constructor_switch = EditConstructorSwitchForm(
|
||||||
|
request.POST or None,
|
||||||
|
instance=constructor_switch
|
||||||
|
)
|
||||||
if constructor_switch.is_valid():
|
if constructor_switch.is_valid():
|
||||||
if constructor_switch.changed_data:
|
if constructor_switch.changed_data:
|
||||||
constructor_switch.save()
|
constructor_switch.save()
|
||||||
messages.success(request, "Le modèle a bien été modifié")
|
messages.success(request, "Le modèle a bien été modifié")
|
||||||
return redirect(reverse('topologie:index-model-switch'))
|
return redirect(reverse('topologie:index-model-switch'))
|
||||||
return form({'topoform': constructor_switch, 'action_name' : 'Editer'}, 'topologie/topo.html', request)
|
return form(
|
||||||
|
{'topoform': constructor_switch, 'action_name': 'Editer'},
|
||||||
|
'topologie/topo.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_delete(ConstructorSwitch)
|
@can_delete(ConstructorSwitch)
|
||||||
def del_constructor_switch(request, constructor_switch, constructorswitchid):
|
def del_constructor_switch(request, constructor_switch, **_kwargs):
|
||||||
""" Suppression d'un constructeur de switch"""
|
""" Suppression d'un constructeur de switch"""
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
try:
|
try:
|
||||||
constructor_switch.delete()
|
constructor_switch.delete()
|
||||||
messages.success(request, "Le constructeur a été détruit")
|
messages.success(request, "Le constructeur a été détruit")
|
||||||
except ProtectedError:
|
except ProtectedError:
|
||||||
messages.error(request, "Le constructeur %s est affecté à un autre objet,\
|
messages.error(
|
||||||
impossible de la supprimer (switch ou user)" % constructor_switch)
|
request,
|
||||||
|
("Le constructeur %s est affecté à un autre objet, impossible "
|
||||||
|
"de la supprimer (switch ou user)" % constructor_switch)
|
||||||
|
)
|
||||||
return redirect(reverse('topologie:index-model-switch'))
|
return redirect(reverse('topologie:index-model-switch'))
|
||||||
return form({
|
return form(
|
||||||
'objet': constructor_switch,
|
{'objet': constructor_switch, 'objet_name': 'Constructeur de switch'},
|
||||||
'objet_name': 'Constructeur de switch'
|
'topologie/delete.html',
|
||||||
}, 'topologie/delete.html', request)
|
request
|
||||||
|
)
|
||||||
|
|
|
@ -20,5 +20,12 @@
|
||||||
# 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.
|
||||||
|
"""users
|
||||||
|
The app managing everything related to the users such as personal
|
||||||
|
informations or the right groups.
|
||||||
|
This is probably the most central app. It is strongly linked with
|
||||||
|
all the other because a user has devices (machines), a cotisation
|
||||||
|
(cotisations), a room (topologie)
|
||||||
|
"""
|
||||||
|
|
||||||
from .acl import *
|
from .acl import *
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
Here are defined some functions to check acl on the application.
|
Here are defined some functions to check acl on the application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def can_view(user):
|
def can_view(user):
|
||||||
"""Check if an user can view the application.
|
"""Check if an user can view the application.
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ class UserAdmin(VersionAdmin, BaseUserAdmin):
|
||||||
)
|
)
|
||||||
# Need to reset the settings from BaseUserAdmin
|
# Need to reset the settings from BaseUserAdmin
|
||||||
# They are using fields we don't use like 'is_staff'
|
# They are using fields we don't use like 'is_staff'
|
||||||
list_filter=()
|
list_filter = ()
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {'fields': ('pseudo', 'password')}),
|
(None, {'fields': ('pseudo', 'password')}),
|
||||||
(
|
(
|
||||||
|
|
|
@ -41,6 +41,10 @@ from django.utils import timezone
|
||||||
from django.contrib.auth.models import Group, Permission
|
from django.contrib.auth.models import Group, Permission
|
||||||
|
|
||||||
from preferences.models import OptionalUser
|
from preferences.models import OptionalUser
|
||||||
|
from re2o.utils import remove_user_room
|
||||||
|
from re2o.mixins import FormRevMixin
|
||||||
|
from re2o.field_permissions import FieldPermissionFormMixin
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
User,
|
User,
|
||||||
ServiceUser,
|
ServiceUser,
|
||||||
|
@ -52,9 +56,6 @@ from .models import (
|
||||||
Adherent,
|
Adherent,
|
||||||
Club
|
Club
|
||||||
)
|
)
|
||||||
from re2o.utils import remove_user_room
|
|
||||||
from re2o.mixins import FormRevMixin
|
|
||||||
from re2o.field_permissions import FieldPermissionFormMixin
|
|
||||||
|
|
||||||
|
|
||||||
class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm):
|
class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm):
|
||||||
|
@ -89,12 +90,16 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm):
|
||||||
password1 = self.cleaned_data.get("passwd1")
|
password1 = self.cleaned_data.get("passwd1")
|
||||||
password2 = self.cleaned_data.get("passwd2")
|
password2 = self.cleaned_data.get("passwd2")
|
||||||
if password1 and password2 and password1 != password2:
|
if password1 and password2 and password1 != password2:
|
||||||
raise forms.ValidationError("Les 2 nouveaux mots de passe sont différents")
|
raise forms.ValidationError(
|
||||||
|
"Les 2 nouveaux mots de passe sont différents"
|
||||||
|
)
|
||||||
return password2
|
return password2
|
||||||
|
|
||||||
def clean_selfpasswd(self):
|
def clean_selfpasswd(self):
|
||||||
"""Verifie si il y a lieu que le mdp self est correct"""
|
"""Verifie si il y a lieu que le mdp self est correct"""
|
||||||
if not self.instance.check_password(self.cleaned_data.get("selfpasswd")):
|
if not self.instance.check_password(
|
||||||
|
self.cleaned_data.get("selfpasswd")
|
||||||
|
):
|
||||||
raise forms.ValidationError("Le mot de passe actuel est incorrect")
|
raise forms.ValidationError("Le mot de passe actuel est incorrect")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -386,7 +391,11 @@ class ClubAdminandMembersForm(FormRevMixin, ModelForm):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(ClubAdminandMembersForm, self).__init__(*args, prefix=prefix, **kwargs)
|
super(ClubAdminandMembersForm, self).__init__(
|
||||||
|
*args,
|
||||||
|
prefix=prefix,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PasswordForm(FormRevMixin, ModelForm):
|
class PasswordForm(FormRevMixin, ModelForm):
|
||||||
|
|
|
@ -19,12 +19,14 @@
|
||||||
# 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.
|
||||||
|
|
||||||
import os, pwd
|
import os
|
||||||
|
import pwd
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from users.forms import PassForm
|
from users.forms import PassForm
|
||||||
from re2o.script_utils import get_user, get_system_user, form_cli
|
from re2o.script_utils import get_user, get_system_user, form_cli
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = "Changer le mot de passe d'un utilisateur"
|
help = "Changer le mot de passe d'un utilisateur"
|
||||||
|
|
||||||
|
@ -42,6 +44,13 @@ class Command(BaseCommand):
|
||||||
if not ok:
|
if not ok:
|
||||||
raise CommandError(msg)
|
raise CommandError(msg)
|
||||||
|
|
||||||
self.stdout.write("Changement du mot de passe de %s" % target_user.pseudo)
|
self.stdout.write(
|
||||||
|
"Changement du mot de passe de %s" % target_user.pseudo
|
||||||
|
)
|
||||||
|
|
||||||
form_cli(PassForm,current_user,"Changement du mot de passe",instance=target_user)
|
form_cli(
|
||||||
|
PassForm,
|
||||||
|
current_user,
|
||||||
|
"Changement du mot de passe",
|
||||||
|
instance=target_user
|
||||||
|
)
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
# 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.
|
||||||
|
|
||||||
import os, sys, pwd
|
import os
|
||||||
|
import sys
|
||||||
|
import pwd
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
@ -28,6 +30,7 @@ from reversion import revisions as reversion
|
||||||
from users.models import User, ListShell
|
from users.models import User, ListShell
|
||||||
from re2o.script_utils import get_user, get_system_user
|
from re2o.script_utils import get_user, get_system_user
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Change the default shell of a user'
|
help = 'Change the default shell of a user'
|
||||||
|
|
||||||
|
@ -36,13 +39,13 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
|
|
||||||
current_username = get_system_user()
|
current_username = get_system_user()
|
||||||
current_user = get_user(current_username)
|
current_user = get_user(current_username)
|
||||||
|
|
||||||
target_username = options["target_username"] or current_username
|
target_username = options["target_username"] or current_username
|
||||||
target_user = get_user(target_username)
|
target_user = get_user(target_username)
|
||||||
|
|
||||||
#L'utilisateur n'a pas le droit de changer le shell
|
# L'utilisateur n'a pas le droit de changer le shell
|
||||||
ok, msg = target_user.can_change_shell(current_user)
|
ok, msg = target_user.can_change_shell(current_user)
|
||||||
if not ok:
|
if not ok:
|
||||||
raise CommandError(msg)
|
raise CommandError(msg)
|
||||||
|
@ -52,9 +55,16 @@ class Command(BaseCommand):
|
||||||
current_shell = "inconnu"
|
current_shell = "inconnu"
|
||||||
if target_user.shell:
|
if target_user.shell:
|
||||||
current_shell = target_user.shell.get_pretty_name()
|
current_shell = target_user.shell.get_pretty_name()
|
||||||
self.stdout.write("Choisissez un shell pour l'utilisateur %s (le shell actuel est %s) :" % (target_user.pseudo, current_shell))
|
self.stdout.write(
|
||||||
|
"Choisissez un shell pour l'utilisateur %s (le shell actuel est "
|
||||||
|
"%s) :" % (target_user.pseudo, current_shell)
|
||||||
|
)
|
||||||
for shell in shells:
|
for shell in shells:
|
||||||
self.stdout.write("%d - %s (%s)" % (shell.id, shell.get_pretty_name(), shell.shell))
|
self.stdout.write("%d - %s (%s)" % (
|
||||||
|
shell.id,
|
||||||
|
shell.get_pretty_name(),
|
||||||
|
shell.shell
|
||||||
|
))
|
||||||
shell_id = input("Entrez un nombre : ")
|
shell_id = input("Entrez un nombre : ")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -72,4 +82,7 @@ class Command(BaseCommand):
|
||||||
reversion.set_user(current_user)
|
reversion.set_user(current_user)
|
||||||
reversion.set_comment("Shell modifié")
|
reversion.set_comment("Shell modifié")
|
||||||
|
|
||||||
self.stdout.write(self.style.SUCCESS("Shell modifié. La modification peut prendre quelques minutes pour s'appliquer."))
|
self.stdout.write(self.style.SUCCESS(
|
||||||
|
"Shell modifié. La modification peut prendre quelques minutes "
|
||||||
|
"pour s'appliquer."
|
||||||
|
))
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
# quelques clics.
|
# quelques clics.
|
||||||
#
|
#
|
||||||
# Copyright © 2018 Benjamin Graillot
|
# Copyright © 2018 Benjamin Graillot
|
||||||
#
|
|
||||||
# Copyright © 2013-2015 Raphaël-David Lasseri <lasseri@crans.org>
|
# Copyright © 2013-2015 Raphaël-David Lasseri <lasseri@crans.org>
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -32,7 +31,8 @@ from users.models import User
|
||||||
|
|
||||||
# Une liste d'expressions régulières à chercher dans les logs.
|
# Une liste d'expressions régulières à chercher dans les logs.
|
||||||
# Elles doivent contenir un groupe 'date' et un groupe 'user'.
|
# Elles doivent contenir un groupe 'date' et un groupe 'user'.
|
||||||
# Pour le CAS on prend comme entrée cat ~/cas.log | grep -B 2 -A 2 "ACTION: AUTHENTICATION_SUCCESS"| grep 'WHEN\|WHO'|sed 'N;s/\n/ /'
|
# Pour le CAS on prend comme entrée
|
||||||
|
# cat ~/cas.log | grep -B 2 -A 2 "ACTION: AUTHENTICATION_SUCCESS"| grep 'WHEN\|WHO'|sed 'N;s/\n/ /'
|
||||||
COMPILED_REGEX = map(re.compile, [
|
COMPILED_REGEX = map(re.compile, [
|
||||||
r'^(?P<date>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}).*(?:'r'dovecot.*Login: user=<|'r'sshd.*Accepted.*for 'r')(?P<user>[^ >]+).*$',
|
r'^(?P<date>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}).*(?:'r'dovecot.*Login: user=<|'r'sshd.*Accepted.*for 'r')(?P<user>[^ >]+).*$',
|
||||||
r'^(?P<date>.*) LOGIN INFO User logged in : (?P<user>.*)',
|
r'^(?P<date>.*) LOGIN INFO User logged in : (?P<user>.*)',
|
||||||
|
@ -48,8 +48,10 @@ DATE_FORMATS = [
|
||||||
"%a %b %d CEST %H:%M:%S%Y"
|
"%a %b %d CEST %H:%M:%S%Y"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Update the time of the latest connection for users by matching stdin against a set of regular expressions'
|
help = ('Update the time of the latest connection for users by matching '
|
||||||
|
'stdin against a set of regular expressions')
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
|
|
||||||
|
@ -65,7 +67,9 @@ class Command(BaseCommand):
|
||||||
for i, regex in enumerate(COMPILED_REGEX):
|
for i, regex in enumerate(COMPILED_REGEX):
|
||||||
m = regex.match(line)
|
m = regex.match(line)
|
||||||
if m:
|
if m:
|
||||||
parsed_log[m.group('user')] = make_aware(datetime.strptime(m.group('date'), DATE_FORMATS[i]))
|
parsed_log[m.group('user')] = make_aware(
|
||||||
|
datetime.strptime(m.group('date'), DATE_FORMATS[i])
|
||||||
|
)
|
||||||
return parsed_log
|
return parsed_log
|
||||||
|
|
||||||
parsed_log = parse_logs(sys.stdin)
|
parsed_log = parse_logs(sys.stdin)
|
||||||
|
|
|
@ -7,8 +7,11 @@ from users.models import User
|
||||||
|
|
||||||
UTC = pytz.timezone('UTC')
|
UTC = pytz.timezone('UTC')
|
||||||
|
|
||||||
|
|
||||||
|
# TODO : remove of finsihed this because currently it should
|
||||||
|
# be failing! Who commited that ?!
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
commands = ['email_remainder',]
|
commands = ['email_remainder']
|
||||||
args = '[command]'
|
args = '[command]'
|
||||||
help = 'Send email remainders'
|
help = 'Send email remainders'
|
||||||
|
|
||||||
|
@ -27,6 +30,6 @@ class Command(BaseCommand):
|
||||||
elif remaining.days == 1:
|
elif remaining.days == 1:
|
||||||
last_day_reminder()
|
last_day_reminder()
|
||||||
|
|
||||||
|
|
||||||
def month_reminder():
|
def month_reminder():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ from django.core.management.base import BaseCommand, CommandError
|
||||||
|
|
||||||
from users.models import User
|
from users.models import User
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Synchronise le ldap à partir du sql. A utiliser dans un cron'
|
help = 'Synchronise le ldap à partir du sql. A utiliser dans un cron'
|
||||||
|
|
||||||
|
@ -37,4 +38,3 @@ class Command(BaseCommand):
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
for usr in User.objects.all():
|
for usr in User.objects.all():
|
||||||
usr.ldap_sync(mac_refresh=options['full'])
|
usr.ldap_sync(mac_refresh=options['full'])
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,6 @@ class Migration(migrations.Migration):
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='user',
|
model_name='user',
|
||||||
name='uid_number',
|
name='uid_number',
|
||||||
field=models.IntegerField(unique=True, default=users.models.User.auto_uid),
|
field=models.IntegerField(unique=True, default=users.models.get_fresh_user_uid),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -32,6 +32,6 @@ class Migration(migrations.Migration):
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='user',
|
model_name='user',
|
||||||
name='uid_number',
|
name='uid_number',
|
||||||
field=models.PositiveIntegerField(default=users.models.User.auto_uid, unique=True),
|
field=models.PositiveIntegerField(default=users.models.get_fresh_user_uid, unique=True),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
21
users/migrations/0071_auto_20180415_1252.py
Normal file
21
users/migrations/0071_auto_20180415_1252.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2018-04-15 10:52
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0070_auto_20180324_1906'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='listright',
|
||||||
|
name='unix_name',
|
||||||
|
field=models.CharField(max_length=255, unique=True, validators=[django.core.validators.RegexValidator('^[a-z]+$', message='Les groupes unix ne peuvent contenir que des lettres minuscules')]),
|
||||||
|
),
|
||||||
|
]
|
361
users/models.py
361
users/models.py
|
@ -153,7 +153,7 @@ class UserManager(BaseUserManager):
|
||||||
|
|
||||||
user.set_password(password)
|
user.set_password(password)
|
||||||
if su:
|
if su:
|
||||||
user.is_superuser=True
|
user.is_superuser = True
|
||||||
user.save(using=self._db)
|
user.save(using=self._db)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
@ -171,7 +171,9 @@ class UserManager(BaseUserManager):
|
||||||
"""
|
"""
|
||||||
return self._create_user(pseudo, surname, email, password, True)
|
return self._create_user(pseudo, surname, email, password, True)
|
||||||
|
|
||||||
class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMixin, AclMixin):
|
|
||||||
|
class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser,
|
||||||
|
PermissionsMixin, AclMixin):
|
||||||
""" Definition de l'utilisateur de base.
|
""" Definition de l'utilisateur de base.
|
||||||
Champs principaux : name, surnname, pseudo, email, room, password
|
Champs principaux : name, surnname, pseudo, email, room, password
|
||||||
Herite du django BaseUser et du système d'auth django"""
|
Herite du django BaseUser et du système d'auth django"""
|
||||||
|
@ -185,10 +187,6 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
(2, 'STATE_ARCHIVE'),
|
(2, 'STATE_ARCHIVE'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def auto_uid():
|
|
||||||
"""Renvoie un uid libre"""
|
|
||||||
return get_fresh_user_uid()
|
|
||||||
|
|
||||||
surname = models.CharField(max_length=255)
|
surname = models.CharField(max_length=255)
|
||||||
pseudo = models.CharField(
|
pseudo = models.CharField(
|
||||||
max_length=32,
|
max_length=32,
|
||||||
|
@ -218,8 +216,15 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
state = models.IntegerField(choices=STATES, default=STATE_ACTIVE)
|
state = models.IntegerField(choices=STATES, default=STATE_ACTIVE)
|
||||||
registered = models.DateTimeField(auto_now_add=True)
|
registered = models.DateTimeField(auto_now_add=True)
|
||||||
telephone = models.CharField(max_length=15, blank=True, null=True)
|
telephone = models.CharField(max_length=15, blank=True, null=True)
|
||||||
uid_number = models.PositiveIntegerField(default=auto_uid, unique=True)
|
uid_number = models.PositiveIntegerField(
|
||||||
rezo_rez_uid = models.PositiveIntegerField(unique=True, blank=True, null=True)
|
default=get_fresh_user_uid,
|
||||||
|
unique=True
|
||||||
|
)
|
||||||
|
rezo_rez_uid = models.PositiveIntegerField(
|
||||||
|
unique=True,
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
USERNAME_FIELD = 'pseudo'
|
USERNAME_FIELD = 'pseudo'
|
||||||
REQUIRED_FIELDS = ['surname', 'email']
|
REQUIRED_FIELDS = ['surname', 'email']
|
||||||
|
@ -228,13 +233,18 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
("change_user_password", "Peut changer le mot de passe d'un user"),
|
("change_user_password",
|
||||||
|
"Peut changer le mot de passe d'un user"),
|
||||||
("change_user_state", "Peut éditer l'etat d'un user"),
|
("change_user_state", "Peut éditer l'etat d'un user"),
|
||||||
("change_user_force", "Peut forcer un déménagement"),
|
("change_user_force", "Peut forcer un déménagement"),
|
||||||
("change_user_shell", "Peut éditer le shell d'un user"),
|
("change_user_shell", "Peut éditer le shell d'un user"),
|
||||||
("change_user_groups", "Peut éditer les groupes d'un user ! Permission critique"),
|
("change_user_groups",
|
||||||
("change_all_users", "Peut éditer tous les users, y compris ceux dotés de droits. Superdroit"),
|
"Peut éditer les groupes d'un user ! Permission critique"),
|
||||||
("view_user", "Peut voir un objet user quelquonque"),
|
("change_all_users",
|
||||||
|
"Peut éditer tous les users, y compris ceux dotés de droits. "
|
||||||
|
"Superdroit"),
|
||||||
|
("view_user",
|
||||||
|
"Peut voir un objet user quelquonque"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -267,10 +277,14 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def is_class_club(self):
|
def is_class_club(self):
|
||||||
|
""" Returns True if the object is a Club (subclassing User) """
|
||||||
|
# TODO : change to isinstance (cleaner)
|
||||||
return hasattr(self, 'club')
|
return hasattr(self, 'club')
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def is_class_adherent(self):
|
def is_class_adherent(self):
|
||||||
|
""" Returns True if the object is a Adherent (subclassing User) """
|
||||||
|
# TODO : change to isinstance (cleaner)
|
||||||
return hasattr(self, 'adherent')
|
return hasattr(self, 'adherent')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -286,7 +300,7 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
@property
|
@property
|
||||||
def is_admin(self):
|
def is_admin(self):
|
||||||
""" Renvoie si l'user est admin"""
|
""" Renvoie si l'user est admin"""
|
||||||
admin,_ = Group.objects.get_or_create(name="admin")
|
admin, _ = Group.objects.get_or_create(name="admin")
|
||||||
return self.is_superuser or admin in self.groups.all()
|
return self.is_superuser or admin in self.groups.all()
|
||||||
|
|
||||||
def get_full_name(self):
|
def get_full_name(self):
|
||||||
|
@ -393,8 +407,9 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
|
|
||||||
def has_access(self):
|
def has_access(self):
|
||||||
""" Renvoie si un utilisateur a accès à internet """
|
""" Renvoie si un utilisateur a accès à internet """
|
||||||
return self.state == User.STATE_ACTIVE\
|
return (self.state == User.STATE_ACTIVE and
|
||||||
and not self.is_ban() and (self.is_connected() or self.is_whitelisted())
|
not self.is_ban() and
|
||||||
|
(self.is_connected() or self.is_whitelisted()))
|
||||||
|
|
||||||
def end_access(self):
|
def end_access(self):
|
||||||
""" Renvoie la date de fin normale d'accès (adhésion ou whiteliste)"""
|
""" Renvoie la date de fin normale d'accès (adhésion ou whiteliste)"""
|
||||||
|
@ -480,7 +495,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
self.assign_ips()
|
self.assign_ips()
|
||||||
self.state = User.STATE_ACTIVE
|
self.state = User.STATE_ACTIVE
|
||||||
|
|
||||||
def ldap_sync(self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False):
|
def ldap_sync(self, base=True, access_refresh=True, mac_refresh=True,
|
||||||
|
group_refresh=False):
|
||||||
""" Synchronisation du ldap. Synchronise dans le ldap les attributs de
|
""" Synchronisation du ldap. Synchronise dans le ldap les attributs de
|
||||||
self
|
self
|
||||||
Options : base : synchronise tous les attributs de base - nom, prenom,
|
Options : base : synchronise tous les attributs de base - nom, prenom,
|
||||||
|
@ -573,12 +589,15 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
'asso_mail': AssoOption.get_cached_value('contact'),
|
'asso_mail': AssoOption.get_cached_value('contact'),
|
||||||
'site_name': GeneralOption.get_cached_value('site_name'),
|
'site_name': GeneralOption.get_cached_value('site_name'),
|
||||||
'url': request.build_absolute_uri(
|
'url': request.build_absolute_uri(
|
||||||
reverse('users:process', kwargs={'token': req.token})),
|
reverse('users:process', kwargs={'token': req.token})
|
||||||
'expire_in': str(GeneralOption.get_cached_value('req_expire_hrs')) + ' heures',
|
),
|
||||||
}
|
'expire_in': str(
|
||||||
|
GeneralOption.get_cached_value('req_expire_hrs')
|
||||||
|
) + ' heures',
|
||||||
|
}
|
||||||
send_mail(
|
send_mail(
|
||||||
'Changement de mot de passe du %(name)s / Password\
|
'Changement de mot de passe du %(name)s / Password renewal for '
|
||||||
renewal for %(name)s' % {'name': AssoOption.get_cached_value('name')},
|
'%(name)s' % {'name': AssoOption.get_cached_value('name')},
|
||||||
template.render(context),
|
template.render(context),
|
||||||
GeneralOption.get_cached_value('email_from'),
|
GeneralOption.get_cached_value('email_from'),
|
||||||
[req.user.email],
|
[req.user.email],
|
||||||
|
@ -590,7 +609,9 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
""" Fonction appellée par freeradius. Enregistre la mac pour
|
""" Fonction appellée par freeradius. Enregistre la mac pour
|
||||||
une machine inconnue sur le compte de l'user"""
|
une machine inconnue sur le compte de l'user"""
|
||||||
all_interfaces = self.user_interfaces(active=False)
|
all_interfaces = self.user_interfaces(active=False)
|
||||||
if all_interfaces.count() > OptionalMachine.get_cached_value('max_lambdauser_interfaces'):
|
if all_interfaces.count() > OptionalMachine.get_cached_value(
|
||||||
|
'max_lambdauser_interfaces'
|
||||||
|
):
|
||||||
return False, "Maximum de machines enregistrees atteinte"
|
return False, "Maximum de machines enregistrees atteinte"
|
||||||
if not nas_type:
|
if not nas_type:
|
||||||
return False, "Re2o ne sait pas à quel machinetype affecter cette\
|
return False, "Re2o ne sait pas à quel machinetype affecter cette\
|
||||||
|
@ -625,9 +646,9 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
template = loader.get_template('users/email_auto_newmachine')
|
template = loader.get_template('users/email_auto_newmachine')
|
||||||
context = Context({
|
context = Context({
|
||||||
'nom': self.get_full_name(),
|
'nom': self.get_full_name(),
|
||||||
'mac_address' : interface.mac_address,
|
'mac_address': interface.mac_address,
|
||||||
'asso_name': AssoOption.get_cached_value('name'),
|
'asso_name': AssoOption.get_cached_value('name'),
|
||||||
'interface_name' : interface.domain,
|
'interface_name': interface.domain,
|
||||||
'asso_email': AssoOption.get_cached_value('contact'),
|
'asso_email': AssoOption.get_cached_value('contact'),
|
||||||
'pseudo': self.pseudo,
|
'pseudo': self.pseudo,
|
||||||
})
|
})
|
||||||
|
@ -668,19 +689,19 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
num += 1
|
num += 1
|
||||||
return composed_pseudo(num)
|
return composed_pseudo(num)
|
||||||
|
|
||||||
def can_edit(self, user_request, *args, **kwargs):
|
def can_edit(self, user_request, *_args, **_kwargs):
|
||||||
"""Check if an user can edit an user object.
|
"""Check if a user can edit a user object.
|
||||||
|
|
||||||
:param self: The user which is to be edited.
|
:param self: The user which is to be edited.
|
||||||
:param user_request: The user who requests to edit self.
|
:param user_request: The user who requests to edit self.
|
||||||
:return: a message and a boolean which is True if self is a club and
|
:return: a message and a boolean which is True if self is a club and
|
||||||
user_request one of its member, or if user_request is self, or if
|
user_request one of its member, or if user_request is self, or if
|
||||||
user_request has the 'cableur' right.
|
user_request has the 'cableur' right.
|
||||||
"""
|
"""
|
||||||
if self.is_class_club and user_request.is_class_adherent:
|
if self.is_class_club and user_request.is_class_adherent:
|
||||||
if self == user_request or \
|
if (self == user_request or
|
||||||
user_request.has_perm('users.change_user') or \
|
user_request.has_perm('users.change_user') or
|
||||||
user_request.adherent in self.club.administrators.all():
|
user_request.adherent in self.club.administrators.all()):
|
||||||
return True, None
|
return True, None
|
||||||
else:
|
else:
|
||||||
return False, u"Vous n'avez pas le droit d'éditer ce club"
|
return False, u"Vous n'avez pas le droit d'éditer ce club"
|
||||||
|
@ -691,98 +712,162 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
return True, None
|
return True, None
|
||||||
elif user_request.has_perm('users.change_user'):
|
elif user_request.has_perm('users.change_user'):
|
||||||
if self.groups.filter(listright__critical=True):
|
if self.groups.filter(listright__critical=True):
|
||||||
return False, u"Utilisateurs avec droits critiques, ne peut etre édité"
|
return False, (u"Utilisateurs avec droits critiques, ne "
|
||||||
|
"peut etre édité")
|
||||||
elif self == AssoOption.get_cached_value('utilisateur_asso'):
|
elif self == AssoOption.get_cached_value('utilisateur_asso'):
|
||||||
return False, u"Impossible d'éditer l'utilisateur asso sans droit change_all_users"
|
return False, (u"Impossible d'éditer l'utilisateur asso "
|
||||||
|
"sans droit change_all_users")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
elif user_request.has_perm('users.change_all_users'):
|
elif user_request.has_perm('users.change_all_users'):
|
||||||
return True, None
|
return True, None
|
||||||
else:
|
else:
|
||||||
return False, u"Vous ne pouvez éditer un autre utilisateur que vous même"
|
return False, (u"Vous ne pouvez éditer un autre utilisateur "
|
||||||
|
"que vous même")
|
||||||
|
|
||||||
def can_change_password(self, user_request, *args, **kwargs):
|
def can_change_password(self, user_request, *_args, **_kwargs):
|
||||||
|
"""Check if a user can change a user's password
|
||||||
|
|
||||||
|
:param self: The user which is to be edited
|
||||||
|
:param user_request: The user who request to edit self
|
||||||
|
:returns: a message and a boolean which is True if self is a club
|
||||||
|
and user_request one of it's admins, or if user_request is self,
|
||||||
|
or if user_request has the right to change other's password
|
||||||
|
"""
|
||||||
if self.is_class_club and user_request.is_class_adherent:
|
if self.is_class_club and user_request.is_class_adherent:
|
||||||
if self == user_request or \
|
if (self == user_request or
|
||||||
user_request.has_perm('users.change_user_password') or \
|
user_request.has_perm('users.change_user_password') or
|
||||||
user_request.adherent in self.club.administrators.all():
|
user_request.adherent in self.club.administrators.all()):
|
||||||
return True, None
|
return True, None
|
||||||
else:
|
else:
|
||||||
return False, u"Vous n'avez pas le droit d'éditer ce club"
|
return False, u"Vous n'avez pas le droit d'éditer ce club"
|
||||||
else:
|
else:
|
||||||
if self == user_request or \
|
if (self == user_request or
|
||||||
user_request.has_perm('users.change_user_groups'):
|
user_request.has_perm('users.change_user_groups')):
|
||||||
# Peut éditer les groupes d'un user, c'est un privilège élevé, True
|
# Peut éditer les groupes d'un user,
|
||||||
|
# c'est un privilège élevé, True
|
||||||
return True, None
|
return True, None
|
||||||
elif user_request.has_perm('users.change_user') and not self.groups.all():
|
elif (user_request.has_perm('users.change_user') and
|
||||||
|
not self.groups.all()):
|
||||||
return True, None
|
return True, None
|
||||||
else:
|
else:
|
||||||
return False, u"Vous ne pouvez éditer un autre utilisateur que vous même"
|
return False, (u"Vous ne pouvez éditer un autre utilisateur "
|
||||||
|
"que vous même")
|
||||||
|
|
||||||
def check_selfpasswd(self, user_request, *args, **kwargs):
|
def check_selfpasswd(self, user_request, *_args, **_kwargs):
|
||||||
|
""" Returns (True, None) if user_request is self, else returns
|
||||||
|
(False, None)
|
||||||
|
"""
|
||||||
return user_request == self, None
|
return user_request == self, None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_change_state(user_request, *args, **kwargs):
|
def can_change_state(user_request, *_args, **_kwargs):
|
||||||
return user_request.has_perm('users.change_user_state'), "Droit requis pour changer l'état"
|
""" Check if a user can change a state
|
||||||
|
|
||||||
|
:param user_request: The user who request
|
||||||
|
:returns: a message and a boolean which is True if the user has
|
||||||
|
the right to change a state
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
user_request.has_perm('users.change_user_state'),
|
||||||
|
"Droit requis pour changer l'état"
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_change_shell(user_request, *args, **kwargs):
|
def can_change_shell(user_request, *_args, **_kwargs):
|
||||||
return user_request.has_perm('users.change_user_shell'), "Droit requis pour changer le shell"
|
""" Check if a user can change a shell
|
||||||
|
|
||||||
|
:param user_request: The user who request
|
||||||
|
:returns: a message and a boolean which is True if the user has
|
||||||
|
the right to change a shell
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
user_request.has_perm('users.change_user_shell'),
|
||||||
|
"Droit requis pour changer le shell"
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_change_force(user_request, *args, **kwargs):
|
def can_change_force(user_request, *_args, **_kwargs):
|
||||||
return user_request.has_perm('users.change_user_force'), "Droit requis pour forcer le déménagement"
|
""" Check if a user can change a force
|
||||||
|
|
||||||
|
:param user_request: The user who request
|
||||||
|
:returns: a message and a boolean which is True if the user has
|
||||||
|
the right to change a force
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
user_request.has_perm('users.change_user_force'),
|
||||||
|
"Droit requis pour forcer le déménagement"
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def can_change_groups(user_request, *args, **kwargs):
|
def can_change_groups(user_request, *_args, **_kwargs):
|
||||||
return user_request.has_perm('users.change_user_groups'), "Droit requis pour éditer les groupes de l'user"
|
""" Check if a user can change a group
|
||||||
|
|
||||||
def can_view(self, user_request, *args, **kwargs):
|
:param user_request: The user who request
|
||||||
|
:returns: a message and a boolean which is True if the user has
|
||||||
|
the right to change a group
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
user_request.has_perm('users.change_user_groups'),
|
||||||
|
"Droit requis pour éditer les groupes de l'user"
|
||||||
|
)
|
||||||
|
|
||||||
|
def can_view(self, user_request, *_args, **_kwargs):
|
||||||
"""Check if an user can view an user object.
|
"""Check if an user can view an user object.
|
||||||
|
|
||||||
:param self: The targeted user.
|
:param self: The targeted user.
|
||||||
:param user_request: The user who ask for viewing the target.
|
:param user_request: The user who ask for viewing the target.
|
||||||
:return: A boolean telling if the acces is granted and an explanation
|
:return: A boolean telling if the acces is granted and an explanation
|
||||||
text
|
text
|
||||||
"""
|
"""
|
||||||
if self.is_class_club and user_request.is_class_adherent:
|
if self.is_class_club and user_request.is_class_adherent:
|
||||||
if self == user_request or \
|
if (self == user_request or
|
||||||
user_request.has_perm('users.view_user') or \
|
user_request.has_perm('users.view_user') or
|
||||||
user_request.adherent in self.club.administrators.all() or \
|
user_request.adherent in self.club.administrators.all() or
|
||||||
user_request.adherent in self.club.members.all():
|
user_request.adherent in self.club.members.all()):
|
||||||
return True, None
|
return True, None
|
||||||
else:
|
else:
|
||||||
return False, u"Vous n'avez pas le droit de voir ce club"
|
return False, u"Vous n'avez pas le droit de voir ce club"
|
||||||
else:
|
else:
|
||||||
if self == user_request or user_request.has_perm('users.view_user'):
|
if (self == user_request or
|
||||||
|
user_request.has_perm('users.view_user')):
|
||||||
return True, None
|
return True, None
|
||||||
else:
|
else:
|
||||||
return False, u"Vous ne pouvez voir un autre utilisateur que vous même"
|
return False, (u"Vous ne pouvez voir un autre utilisateur "
|
||||||
|
"que vous même")
|
||||||
|
|
||||||
def can_view_all(user_request, *args, **kwargs):
|
@staticmethod
|
||||||
|
def can_view_all(user_request, *_args, **_kwargs):
|
||||||
"""Check if an user can access to the list of every user objects
|
"""Check if an user can access to the list of every user objects
|
||||||
|
|
||||||
:param user_request: The user who wants to view the list.
|
:param user_request: The user who wants to view the list.
|
||||||
:return: True if the user can view the list and an explanation message.
|
:return: True if the user can view the list and an explanation
|
||||||
|
message.
|
||||||
"""
|
"""
|
||||||
return user_request.has_perm('users.view_user'), u"Vous n'avez pas accès à la liste des utilisateurs."
|
return (
|
||||||
|
user_request.has_perm('users.view_user'),
|
||||||
|
u"Vous n'avez pas accès à la liste des utilisateurs."
|
||||||
|
)
|
||||||
|
|
||||||
def can_delete(self, user_request, *args, **kwargs):
|
def can_delete(self, user_request, *_args, **_kwargs):
|
||||||
"""Check if an user can delete an user object.
|
"""Check if an user can delete an user object.
|
||||||
|
|
||||||
:param self: The user who is to be deleted.
|
:param self: The user who is to be deleted.
|
||||||
:param user_request: The user who requests deletion.
|
:param user_request: The user who requests deletion.
|
||||||
:return: True if user_request has the right 'bureau', and a message.
|
:return: True if user_request has the right 'bureau', and a
|
||||||
|
message.
|
||||||
"""
|
"""
|
||||||
return user_request.has_perm('users.delete_user'), u"Vous ne pouvez pas supprimer cet utilisateur."
|
return (
|
||||||
|
user_request.has_perm('users.delete_user'),
|
||||||
|
u"Vous ne pouvez pas supprimer cet utilisateur."
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(User, self).__init__(*args, **kwargs)
|
super(User, self).__init__(*args, **kwargs)
|
||||||
self.field_permissions = {
|
self.field_permissions = {
|
||||||
'shell' : self.can_change_shell,
|
'shell': self.can_change_shell,
|
||||||
'force' : self.can_change_force,
|
'force': self.can_change_force,
|
||||||
'selfpasswd' : self.check_selfpasswd,
|
'selfpasswd': self.check_selfpasswd,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -790,6 +875,8 @@ class User(RevMixin, FieldPermissionModelMixin, AbstractBaseUser, PermissionsMix
|
||||||
|
|
||||||
|
|
||||||
class Adherent(User):
|
class Adherent(User):
|
||||||
|
""" A class representing a member (it's a user with special
|
||||||
|
informations) """
|
||||||
PRETTY_NAME = "Adhérents"
|
PRETTY_NAME = "Adhérents"
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
room = models.OneToOneField(
|
room = models.OneToOneField(
|
||||||
|
@ -799,32 +886,40 @@ class Adherent(User):
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_instance(adherentid, *args, **kwargs):
|
@classmethod
|
||||||
|
def get_instance(cls, adherentid, *_args, **_kwargs):
|
||||||
"""Try to find an instance of `Adherent` with the given id.
|
"""Try to find an instance of `Adherent` with the given id.
|
||||||
|
|
||||||
:param adherentid: The id of the adherent we are looking for.
|
:param adherentid: The id of the adherent we are looking for.
|
||||||
:return: An adherent.
|
:return: An adherent.
|
||||||
"""
|
"""
|
||||||
return Adherent.objects.get(pk=adherentid)
|
return cls.objects.get(pk=adherentid)
|
||||||
|
|
||||||
def can_create(user_request, *args, **kwargs):
|
@staticmethod
|
||||||
|
def can_create(user_request, *_args, **_kwargs):
|
||||||
"""Check if an user can create an user object.
|
"""Check if an user can create an user object.
|
||||||
|
|
||||||
:param user_request: The user who wants to create a user object.
|
:param user_request: The user who wants to create a user object.
|
||||||
:return: a message and a boolean which is True if the user can create
|
:return: a message and a boolean which is True if the user can create
|
||||||
an user or if the `options.all_can_create` is set.
|
a user or if the `options.all_can_create` is set.
|
||||||
"""
|
"""
|
||||||
if(not user_request.is_authenticated and not OptionalUser.get_cached_value('self_adhesion')):
|
if (not user_request.is_authenticated and
|
||||||
|
not OptionalUser.get_cached_value('self_adhesion')):
|
||||||
return False, None
|
return False, None
|
||||||
else:
|
else:
|
||||||
if(OptionalUser.get_cached_value('all_can_create_adherent') or OptionalUser.get_cached_value('self_adhesion')):
|
if (OptionalUser.get_cached_value('all_can_create_adherent') or
|
||||||
|
OptionalUser.get_cached_value('self_adhesion')):
|
||||||
return True, None
|
return True, None
|
||||||
else:
|
else:
|
||||||
return user_request.has_perm('users.add_user'), u"Vous n'avez pas le\
|
return (
|
||||||
droit de créer un utilisateur"
|
user_request.has_perm('users.add_user'),
|
||||||
|
u"Vous n'avez pas le droit de créer un utilisateur"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Club(User):
|
class Club(User):
|
||||||
|
""" A class representing a club (it is considered as a user
|
||||||
|
with special informations) """
|
||||||
PRETTY_NAME = "Clubs"
|
PRETTY_NAME = "Clubs"
|
||||||
room = models.ForeignKey(
|
room = models.ForeignKey(
|
||||||
'topologie.Room',
|
'topologie.Room',
|
||||||
|
@ -843,15 +938,16 @@ class Club(User):
|
||||||
related_name='club_members'
|
related_name='club_members'
|
||||||
)
|
)
|
||||||
mailing = models.BooleanField(
|
mailing = models.BooleanField(
|
||||||
default = False
|
default=False
|
||||||
)
|
)
|
||||||
|
|
||||||
def can_create(user_request, *args, **kwargs):
|
@staticmethod
|
||||||
|
def can_create(user_request, *_args, **_kwargs):
|
||||||
"""Check if an user can create an user object.
|
"""Check if an user can create an user object.
|
||||||
|
|
||||||
:param user_request: The user who wants to create a user object.
|
:param user_request: The user who wants to create a user object.
|
||||||
:return: a message and a boolean which is True if the user can create
|
:return: a message and a boolean which is True if the user can create
|
||||||
an user or if the `options.all_can_create` is set.
|
an user or if the `options.all_can_create` is set.
|
||||||
"""
|
"""
|
||||||
if not user_request.is_authenticated:
|
if not user_request.is_authenticated:
|
||||||
return False, None
|
return False, None
|
||||||
|
@ -859,54 +955,68 @@ class Club(User):
|
||||||
if OptionalUser.get_cached_value('all_can_create_club'):
|
if OptionalUser.get_cached_value('all_can_create_club'):
|
||||||
return True, None
|
return True, None
|
||||||
else:
|
else:
|
||||||
return user_request.has_perm('users.add_user'), u"Vous n'avez pas le\
|
return (
|
||||||
droit de créer un club"
|
user_request.has_perm('users.add_user'),
|
||||||
|
u"Vous n'avez pas le droit de créer un club"
|
||||||
|
)
|
||||||
|
|
||||||
def can_view_all(user_request, *args, **kwargs):
|
@staticmethod
|
||||||
|
def can_view_all(user_request, *_args, **_kwargs):
|
||||||
"""Check if an user can access to the list of every user objects
|
"""Check if an user can access to the list of every user objects
|
||||||
|
|
||||||
:param user_request: The user who wants to view the list.
|
:param user_request: The user who wants to view the list.
|
||||||
:return: True if the user can view the list and an explanation message.
|
:return: True if the user can view the list and an explanation
|
||||||
|
message.
|
||||||
"""
|
"""
|
||||||
if user_request.has_perm('users.view_user'):
|
if user_request.has_perm('users.view_user'):
|
||||||
return True, None
|
return True, None
|
||||||
if hasattr(user_request,'is_class_adherent') and user_request.is_class_adherent:
|
if (hasattr(user_request, 'is_class_adherent') and
|
||||||
if user_request.adherent.club_administrator.all() or user_request.adherent.club_members.all():
|
user_request.is_class_adherent):
|
||||||
|
if (user_request.adherent.club_administrator.all() or
|
||||||
|
user_request.adherent.club_members.all()):
|
||||||
return True, None
|
return True, None
|
||||||
return False, u"Vous n'avez pas accès à la liste des utilisateurs."
|
return False, u"Vous n'avez pas accès à la liste des utilisateurs."
|
||||||
|
|
||||||
def get_instance(clubid, *args, **kwargs):
|
@classmethod
|
||||||
|
def get_instance(cls, clubid, *_args, **_kwargs):
|
||||||
"""Try to find an instance of `Club` with the given id.
|
"""Try to find an instance of `Club` with the given id.
|
||||||
|
|
||||||
:param clubid: The id of the adherent we are looking for.
|
:param clubid: The id of the adherent we are looking for.
|
||||||
:return: A club.
|
:return: A club.
|
||||||
"""
|
"""
|
||||||
return Club.objects.get(pk=clubid)
|
return cls.objects.get(pk=clubid)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Adherent)
|
@receiver(post_save, sender=Adherent)
|
||||||
@receiver(post_save, sender=Club)
|
@receiver(post_save, sender=Club)
|
||||||
@receiver(post_save, sender=User)
|
@receiver(post_save, sender=User)
|
||||||
def user_post_save(sender, **kwargs):
|
def user_post_save(**kwargs):
|
||||||
""" Synchronisation post_save : envoie le mail de bienvenue si creation
|
""" Synchronisation post_save : envoie le mail de bienvenue si creation
|
||||||
Synchronise le ldap"""
|
Synchronise le ldap"""
|
||||||
is_created = kwargs['created']
|
# is_created = kwargs['created']
|
||||||
user = kwargs['instance']
|
user = kwargs['instance']
|
||||||
#if is_created:
|
# TODO : remove if unnecessary
|
||||||
#user.notif_inscription()
|
# if is_created:
|
||||||
user.ldap_sync(base=True, access_refresh=True, mac_refresh=False, group_refresh=True)
|
# user.notif_inscription()
|
||||||
|
user.ldap_sync(
|
||||||
|
base=True,
|
||||||
|
access_refresh=True,
|
||||||
|
mac_refresh=False,
|
||||||
|
group_refresh=True
|
||||||
|
)
|
||||||
regen('mailing')
|
regen('mailing')
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Adherent)
|
@receiver(post_delete, sender=Adherent)
|
||||||
@receiver(post_delete, sender=Club)
|
@receiver(post_delete, sender=Club)
|
||||||
@receiver(post_delete, sender=User)
|
@receiver(post_delete, sender=User)
|
||||||
def user_post_delete(sender, **kwargs):
|
def user_post_delete(**kwargs):
|
||||||
"""Post delete d'un user, on supprime son instance ldap"""
|
"""Post delete d'un user, on supprime son instance ldap"""
|
||||||
user = kwargs['instance']
|
user = kwargs['instance']
|
||||||
user.ldap_del()
|
user.ldap_del()
|
||||||
regen('mailing')
|
regen('mailing')
|
||||||
|
|
||||||
|
|
||||||
class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
|
class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
|
||||||
""" Classe des users daemons, règle leurs accès au ldap"""
|
""" Classe des users daemons, règle leurs accès au ldap"""
|
||||||
readonly = 'readonly'
|
readonly = 'readonly'
|
||||||
|
@ -943,6 +1053,14 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
|
||||||
("view_serviceuser", "Peut voir un objet serviceuser"),
|
("view_serviceuser", "Peut voir un objet serviceuser"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_full_name(self):
|
||||||
|
""" Renvoie le nom complet du serviceUser formaté nom/prénom"""
|
||||||
|
return "ServiceUser <{name}>".format(name=self.pseudo)
|
||||||
|
|
||||||
|
def get_short_name(self):
|
||||||
|
""" Renvoie seulement le nom"""
|
||||||
|
return self.pseudo
|
||||||
|
|
||||||
def ldap_sync(self):
|
def ldap_sync(self):
|
||||||
""" Synchronisation du ServiceUser dans sa version ldap"""
|
""" Synchronisation du ServiceUser dans sa version ldap"""
|
||||||
try:
|
try:
|
||||||
|
@ -977,15 +1095,16 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.pseudo
|
return self.pseudo
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=ServiceUser)
|
@receiver(post_save, sender=ServiceUser)
|
||||||
def service_user_post_save(sender, **kwargs):
|
def service_user_post_save(**kwargs):
|
||||||
""" Synchronise un service user ldap après modification django"""
|
""" Synchronise un service user ldap après modification django"""
|
||||||
service_user = kwargs['instance']
|
service_user = kwargs['instance']
|
||||||
service_user.ldap_sync()
|
service_user.ldap_sync()
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=ServiceUser)
|
@receiver(post_delete, sender=ServiceUser)
|
||||||
def service_user_post_delete(sender, **kwargs):
|
def service_user_post_delete(**kwargs):
|
||||||
""" Supprime un service user ldap après suppression django"""
|
""" Supprime un service user ldap après suppression django"""
|
||||||
service_user = kwargs['instance']
|
service_user = kwargs['instance']
|
||||||
service_user.ldap_del()
|
service_user.ldap_del()
|
||||||
|
@ -1019,8 +1138,8 @@ class ListRight(RevMixin, AclMixin, Group):
|
||||||
unique=True,
|
unique=True,
|
||||||
validators=[RegexValidator(
|
validators=[RegexValidator(
|
||||||
'^[a-z]+$',
|
'^[a-z]+$',
|
||||||
message="Les groupes unix ne peuvent contenir\
|
message=("Les groupes unix ne peuvent contenir que des lettres "
|
||||||
que des lettres minuscules"
|
"minuscules")
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
gid = models.PositiveIntegerField(unique=True, null=True)
|
gid = models.PositiveIntegerField(unique=True, null=True)
|
||||||
|
@ -1060,14 +1179,14 @@ class ListRight(RevMixin, AclMixin, Group):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=ListRight)
|
@receiver(post_save, sender=ListRight)
|
||||||
def listright_post_save(sender, **kwargs):
|
def listright_post_save(**kwargs):
|
||||||
""" Synchronise le droit ldap quand il est modifié"""
|
""" Synchronise le droit ldap quand il est modifié"""
|
||||||
right = kwargs['instance']
|
right = kwargs['instance']
|
||||||
right.ldap_sync()
|
right.ldap_sync()
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=ListRight)
|
@receiver(post_delete, sender=ListRight)
|
||||||
def listright_post_delete(sender, **kwargs):
|
def listright_post_delete(**kwargs):
|
||||||
"""Suppression d'un groupe ldap après suppression coté django"""
|
"""Suppression d'un groupe ldap après suppression coté django"""
|
||||||
right = kwargs['instance']
|
right = kwargs['instance']
|
||||||
right.ldap_del()
|
right.ldap_del()
|
||||||
|
@ -1140,7 +1259,7 @@ class Ban(RevMixin, AclMixin, models.Model):
|
||||||
"""Ce ban est-il actif?"""
|
"""Ce ban est-il actif?"""
|
||||||
return self.date_end > timezone.now()
|
return self.date_end > timezone.now()
|
||||||
|
|
||||||
def can_view(self, user_request, *args, **kwargs):
|
def can_view(self, user_request, *_args, **_kwargs):
|
||||||
"""Check if an user can view a Ban object.
|
"""Check if an user can view a Ban object.
|
||||||
|
|
||||||
:param self: The targeted object.
|
:param self: The targeted object.
|
||||||
|
@ -1148,10 +1267,10 @@ class Ban(RevMixin, AclMixin, models.Model):
|
||||||
:return: A boolean telling if the acces is granted and an explanation
|
:return: A boolean telling if the acces is granted and an explanation
|
||||||
text
|
text
|
||||||
"""
|
"""
|
||||||
if not user_request.has_perm('users.view_ban') and\
|
if (not user_request.has_perm('users.view_ban') and
|
||||||
self.user != user_request:
|
self.user != user_request):
|
||||||
return False, u"Vous n'avez pas le droit de voir les bannissements\
|
return False, (u"Vous n'avez pas le droit de voir les "
|
||||||
autre que les vôtres"
|
"bannissements autre que les vôtres")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
@ -1160,7 +1279,7 @@ class Ban(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Ban)
|
@receiver(post_save, sender=Ban)
|
||||||
def ban_post_save(sender, **kwargs):
|
def ban_post_save(**kwargs):
|
||||||
""" Regeneration de tous les services après modification d'un ban"""
|
""" Regeneration de tous les services après modification d'un ban"""
|
||||||
ban = kwargs['instance']
|
ban = kwargs['instance']
|
||||||
is_created = kwargs['created']
|
is_created = kwargs['created']
|
||||||
|
@ -1177,7 +1296,7 @@ def ban_post_save(sender, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Ban)
|
@receiver(post_delete, sender=Ban)
|
||||||
def ban_post_delete(sender, **kwargs):
|
def ban_post_delete(**kwargs):
|
||||||
""" Regen de tous les services après suppression d'un ban"""
|
""" Regen de tous les services après suppression d'un ban"""
|
||||||
user = kwargs['instance'].user
|
user = kwargs['instance'].user
|
||||||
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
|
||||||
|
@ -1203,9 +1322,10 @@ class Whitelist(RevMixin, AclMixin, models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
|
""" Is this whitelisting active ? """
|
||||||
return self.date_end > timezone.now()
|
return self.date_end > timezone.now()
|
||||||
|
|
||||||
def can_view(self, user_request, *args, **kwargs):
|
def can_view(self, user_request, *_args, **_kwargs):
|
||||||
"""Check if an user can view a Whitelist object.
|
"""Check if an user can view a Whitelist object.
|
||||||
|
|
||||||
:param self: The targeted object.
|
:param self: The targeted object.
|
||||||
|
@ -1213,10 +1333,10 @@ class Whitelist(RevMixin, AclMixin, models.Model):
|
||||||
:return: A boolean telling if the acces is granted and an explanation
|
:return: A boolean telling if the acces is granted and an explanation
|
||||||
text
|
text
|
||||||
"""
|
"""
|
||||||
if not user_request.has_perm('users.view_whitelist') and\
|
if (not user_request.has_perm('users.view_whitelist') and
|
||||||
self.user != user_request:
|
self.user != user_request):
|
||||||
return False, u"Vous n'avez pas le droit de voir les accès\
|
return False, (u"Vous n'avez pas le droit de voir les accès "
|
||||||
gracieux autre que les vôtres"
|
"gracieux autre que les vôtres")
|
||||||
else:
|
else:
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
@ -1225,7 +1345,7 @@ class Whitelist(RevMixin, AclMixin, models.Model):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Whitelist)
|
@receiver(post_save, sender=Whitelist)
|
||||||
def whitelist_post_save(sender, **kwargs):
|
def whitelist_post_save(**kwargs):
|
||||||
"""Après modification d'une whitelist, on synchronise les services
|
"""Après modification d'une whitelist, on synchronise les services
|
||||||
et on lui permet d'avoir internet"""
|
et on lui permet d'avoir internet"""
|
||||||
whitelist = kwargs['instance']
|
whitelist = kwargs['instance']
|
||||||
|
@ -1242,7 +1362,7 @@ def whitelist_post_save(sender, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_delete, sender=Whitelist)
|
@receiver(post_delete, sender=Whitelist)
|
||||||
def whitelist_post_delete(sender, **kwargs):
|
def whitelist_post_delete(**kwargs):
|
||||||
"""Après suppression d'une whitelist, on supprime l'accès internet
|
"""Après suppression d'une whitelist, on supprime l'accès internet
|
||||||
en forçant la régénration"""
|
en forçant la régénration"""
|
||||||
user = kwargs['instance'].user
|
user = kwargs['instance'].user
|
||||||
|
@ -1270,8 +1390,12 @@ class Request(models.Model):
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
if not self.expires_at:
|
if not self.expires_at:
|
||||||
self.expires_at = timezone.now() \
|
self.expires_at = (timezone.now() +
|
||||||
+ datetime.timedelta(hours=GeneralOption.get_cached_value('req_expire_hrs'))
|
datetime.timedelta(
|
||||||
|
hours=GeneralOption.get_cached_value(
|
||||||
|
'req_expire_hrs'
|
||||||
|
)
|
||||||
|
))
|
||||||
if not self.token:
|
if not self.token:
|
||||||
self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens
|
self.token = str(uuid.uuid4()).replace('-', '') # remove hyphens
|
||||||
super(Request, self).save()
|
super(Request, self).save()
|
||||||
|
@ -1375,7 +1499,10 @@ class LdapUserGroup(ldapdb.models.Model):
|
||||||
|
|
||||||
# attributes
|
# attributes
|
||||||
gid = ldapdb.models.fields.IntegerField(db_column='gidNumber')
|
gid = ldapdb.models.fields.IntegerField(db_column='gidNumber')
|
||||||
members = ldapdb.models.fields.ListField(db_column='memberUid', blank=True)
|
members = ldapdb.models.fields.ListField(
|
||||||
|
db_column='memberUid',
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
name = ldapdb.models.fields.CharField(
|
name = ldapdb.models.fields.CharField(
|
||||||
db_column='cn',
|
db_column='cn',
|
||||||
max_length=200,
|
max_length=200,
|
||||||
|
|
|
@ -20,19 +20,30 @@
|
||||||
# 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.
|
||||||
|
|
||||||
#Maël Kervella
|
# Maël Kervella
|
||||||
|
|
||||||
|
"""users.serializers
|
||||||
|
Serializers for the User app
|
||||||
|
"""
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from users.models import Club, Adherent
|
from users.models import Club, Adherent
|
||||||
|
|
||||||
|
|
||||||
class MailingSerializer(serializers.ModelSerializer):
|
class MailingSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serializer to build Mailing objects """
|
||||||
|
|
||||||
name = serializers.CharField(source='pseudo')
|
name = serializers.CharField(source='pseudo')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Club
|
model = Club
|
||||||
fields = ('name',)
|
fields = ('name',)
|
||||||
|
|
||||||
|
|
||||||
class MailingMemberSerializer(serializers.ModelSerializer):
|
class MailingMemberSerializer(serializers.ModelSerializer):
|
||||||
|
""" Serializer fot the Adherent objects (who belong to a
|
||||||
|
Mailing) """
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Adherent
|
model = Adherent
|
||||||
fields = ('email',)
|
fields = ('email',)
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
# 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.
|
||||||
|
"""users.tests
|
||||||
|
The tests for the Users module.
|
||||||
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
# from django.test import TestCase
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
|
100
users/urls.py
100
users/urls.py
|
@ -34,109 +34,79 @@ urlpatterns = [
|
||||||
url(r'^new_user/$', views.new_user, name='new-user'),
|
url(r'^new_user/$', views.new_user, name='new-user'),
|
||||||
url(r'^new_club/$', views.new_club, name='new-club'),
|
url(r'^new_club/$', views.new_club, name='new-club'),
|
||||||
url(r'^edit_info/(?P<userid>[0-9]+)$', views.edit_info, name='edit-info'),
|
url(r'^edit_info/(?P<userid>[0-9]+)$', views.edit_info, name='edit-info'),
|
||||||
url(
|
url(r'^edit_club_admin_members/(?P<clubid>[0-9]+)$',
|
||||||
r'^edit_club_admin_members/(?P<clubid>[0-9]+)$',
|
|
||||||
views.edit_club_admin_members,
|
views.edit_club_admin_members,
|
||||||
name='edit-club-admin-members'
|
name='edit-club-admin-members'),
|
||||||
),
|
|
||||||
url(r'^state/(?P<userid>[0-9]+)$', views.state, name='state'),
|
url(r'^state/(?P<userid>[0-9]+)$', views.state, name='state'),
|
||||||
url(r'^groups/(?P<userid>[0-9]+)$', views.groups, name='groups'),
|
url(r'^groups/(?P<userid>[0-9]+)$', views.groups, name='groups'),
|
||||||
url(r'^password/(?P<userid>[0-9]+)$', views.password, name='password'),
|
url(r'^password/(?P<userid>[0-9]+)$', views.password, name='password'),
|
||||||
url(r'^del_group/(?P<userid>[0-9]+)/(?P<listrightid>[0-9]+)$', views.del_group, name='del-group'),
|
url(r'^del_group/(?P<userid>[0-9]+)/(?P<listrightid>[0-9]+)$',
|
||||||
|
views.del_group,
|
||||||
|
name='del-group'),
|
||||||
url(r'^new_serviceuser/$', views.new_serviceuser, name='new-serviceuser'),
|
url(r'^new_serviceuser/$', views.new_serviceuser, name='new-serviceuser'),
|
||||||
url(
|
url(r'^edit_serviceuser/(?P<serviceuserid>[0-9]+)$',
|
||||||
r'^edit_serviceuser/(?P<serviceuserid>[0-9]+)$',
|
|
||||||
views.edit_serviceuser,
|
views.edit_serviceuser,
|
||||||
name='edit-serviceuser'
|
name='edit-serviceuser'),
|
||||||
),
|
url(r'^del_serviceuser/(?P<serviceuserid>[0-9]+)$',
|
||||||
url(
|
|
||||||
r'^del_serviceuser/(?P<serviceuserid>[0-9]+)$',
|
|
||||||
views.del_serviceuser,
|
views.del_serviceuser,
|
||||||
name='del-serviceuser'
|
name='del-serviceuser'),
|
||||||
),
|
|
||||||
url(r'^add_ban/(?P<userid>[0-9]+)$', views.add_ban, name='add-ban'),
|
url(r'^add_ban/(?P<userid>[0-9]+)$', views.add_ban, name='add-ban'),
|
||||||
url(r'^edit_ban/(?P<banid>[0-9]+)$', views.edit_ban, name='edit-ban'),
|
url(r'^edit_ban/(?P<banid>[0-9]+)$', views.edit_ban, name='edit-ban'),
|
||||||
url(
|
url(r'^add_whitelist/(?P<userid>[0-9]+)$',
|
||||||
r'^add_whitelist/(?P<userid>[0-9]+)$',
|
|
||||||
views.add_whitelist,
|
views.add_whitelist,
|
||||||
name='add-whitelist'
|
name='add-whitelist'),
|
||||||
),
|
url(r'^edit_whitelist/(?P<whitelistid>[0-9]+)$',
|
||||||
url(
|
|
||||||
r'^edit_whitelist/(?P<whitelistid>[0-9]+)$',
|
|
||||||
views.edit_whitelist,
|
views.edit_whitelist,
|
||||||
name='edit-whitelist'
|
name='edit-whitelist'),
|
||||||
),
|
|
||||||
url(r'^add_school/$', views.add_school, name='add-school'),
|
url(r'^add_school/$', views.add_school, name='add-school'),
|
||||||
url(
|
url(r'^edit_school/(?P<schoolid>[0-9]+)$',
|
||||||
r'^edit_school/(?P<schoolid>[0-9]+)$',
|
|
||||||
views.edit_school,
|
views.edit_school,
|
||||||
name='edit-school'
|
name='edit-school'),
|
||||||
),
|
|
||||||
url(r'^del_school/$', views.del_school, name='del-school'),
|
url(r'^del_school/$', views.del_school, name='del-school'),
|
||||||
url(r'^add_listright/$', views.add_listright, name='add-listright'),
|
url(r'^add_listright/$', views.add_listright, name='add-listright'),
|
||||||
url(
|
url(r'^edit_listright/(?P<listrightid>[0-9]+)$',
|
||||||
r'^edit_listright/(?P<listrightid>[0-9]+)$',
|
|
||||||
views.edit_listright,
|
views.edit_listright,
|
||||||
name='edit-listright'
|
name='edit-listright'),
|
||||||
),
|
|
||||||
url(r'^del_listright/$', views.del_listright, name='del-listright'),
|
url(r'^del_listright/$', views.del_listright, name='del-listright'),
|
||||||
url(r'^add_shell/$', views.add_shell, name='add-shell'),
|
url(r'^add_shell/$', views.add_shell, name='add-shell'),
|
||||||
url(
|
url(r'^edit_shell/(?P<listshellid>[0-9]+)$',
|
||||||
r'^edit_shell/(?P<listshellid>[0-9]+)$',
|
|
||||||
views.edit_shell,
|
views.edit_shell,
|
||||||
name='edit-shell'
|
name='edit-shell'),
|
||||||
),
|
url(r'^del_shell/(?P<listshellid>[0-9]+)$',
|
||||||
url(
|
|
||||||
r'^del_shell/(?P<listshellid>[0-9]+)$',
|
|
||||||
views.del_shell,
|
views.del_shell,
|
||||||
name='del-shell'
|
name='del-shell'),
|
||||||
),
|
|
||||||
url(r'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'),
|
url(r'^profil/(?P<userid>[0-9]+)$', views.profil, name='profil'),
|
||||||
url(r'^index_ban/$', views.index_ban, name='index-ban'),
|
url(r'^index_ban/$', views.index_ban, name='index-ban'),
|
||||||
url(r'^index_white/$', views.index_white, name='index-white'),
|
url(r'^index_white/$', views.index_white, name='index-white'),
|
||||||
url(r'^index_school/$', views.index_school, name='index-school'),
|
url(r'^index_school/$', views.index_school, name='index-school'),
|
||||||
url(r'^index_shell/$', views.index_shell, name='index-shell'),
|
url(r'^index_shell/$', views.index_shell, name='index-shell'),
|
||||||
url(r'^index_listright/$', views.index_listright, name='index-listright'),
|
url(r'^index_listright/$', views.index_listright, name='index-listright'),
|
||||||
url(
|
url(r'^index_serviceusers/$',
|
||||||
r'^index_serviceusers/$',
|
|
||||||
views.index_serviceusers,
|
views.index_serviceusers,
|
||||||
name='index-serviceusers'
|
name='index-serviceusers'),
|
||||||
),
|
|
||||||
url(r'^mon_profil/$', views.mon_profil, name='mon-profil'),
|
url(r'^mon_profil/$', views.mon_profil, name='mon-profil'),
|
||||||
url(r'^process/(?P<token>[a-z0-9]{32})/$', views.process, name='process'),
|
url(r'^process/(?P<token>[a-z0-9]{32})/$', views.process, name='process'),
|
||||||
url(r'^reset_password/$', views.reset_password, name='reset-password'),
|
url(r'^reset_password/$', views.reset_password, name='reset-password'),
|
||||||
url(r'^mass_archive/$', views.mass_archive, name='mass-archive'),
|
url(r'^mass_archive/$', views.mass_archive, name='mass-archive'),
|
||||||
url(
|
url(r'^history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
||||||
r'^history/(?P<object_name>\w+)/(?P<object_id>[0-9]+)$',
|
|
||||||
re2o.views.history,
|
re2o.views.history,
|
||||||
name='history',
|
name='history',
|
||||||
kwargs={'application':'users'},
|
kwargs={'application': 'users'}),
|
||||||
),
|
|
||||||
url(r'^$', views.index, name='index'),
|
url(r'^$', views.index, name='index'),
|
||||||
url(r'^index_clubs/$', views.index_clubs, name='index-clubs'),
|
url(r'^index_clubs/$', views.index_clubs, name='index-clubs'),
|
||||||
url(
|
url(r'^rest/ml/std/$',
|
||||||
r'^rest/ml/std/$',
|
|
||||||
views.ml_std_list,
|
views.ml_std_list,
|
||||||
name='ml-std-list'
|
name='ml-std-list'),
|
||||||
),
|
url(r'^rest/ml/std/member/(?P<ml_name>\w+)/$',
|
||||||
url(
|
|
||||||
r'^rest/ml/std/member/(?P<ml_name>\w+)/$',
|
|
||||||
views.ml_std_members,
|
views.ml_std_members,
|
||||||
name='ml-std-members'
|
name='ml-std-members'),
|
||||||
),
|
url(r'^rest/ml/club/$',
|
||||||
url(
|
|
||||||
r'^rest/ml/club/$',
|
|
||||||
views.ml_club_list,
|
views.ml_club_list,
|
||||||
name='ml-club-list'
|
name='ml-club-list'),
|
||||||
),
|
url(r'^rest/ml/club/admin/(?P<ml_name>\w+)/$',
|
||||||
url(
|
|
||||||
r'^rest/ml/club/admin/(?P<ml_name>\w+)/$',
|
|
||||||
views.ml_club_admins,
|
views.ml_club_admins,
|
||||||
name='ml-club-admins'
|
name='ml-club-admins'),
|
||||||
),
|
url(r'^rest/ml/club/member/(?P<ml_name>\w+)/$',
|
||||||
url(
|
|
||||||
r'^rest/ml/club/member/(?P<ml_name>\w+)/$',
|
|
||||||
views.ml_club_members,
|
views.ml_club_members,
|
||||||
name='ml-club-members'
|
name='ml-club-members'),
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
329
users/views.py
329
users/views.py
|
@ -39,22 +39,37 @@ from django.urls import reverse
|
||||||
from django.shortcuts import get_object_or_404, render, redirect
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.db.models import ProtectedError, Q
|
from django.db.models import ProtectedError
|
||||||
from django.db import IntegrityError
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
|
|
||||||
from rest_framework.renderers import JSONRenderer
|
from rest_framework.renderers import JSONRenderer
|
||||||
|
|
||||||
|
|
||||||
from reversion.models import Version
|
|
||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
from users.serializers import MailingSerializer, MailingMemberSerializer
|
|
||||||
from users.models import (
|
from cotisations.models import Facture
|
||||||
|
from machines.models import Machine
|
||||||
|
from preferences.models import OptionalUser, GeneralOption, AssoOption
|
||||||
|
from re2o.views import form
|
||||||
|
from re2o.utils import (
|
||||||
|
all_has_access,
|
||||||
|
SortTable,
|
||||||
|
re2o_paginator
|
||||||
|
)
|
||||||
|
from re2o.acl import (
|
||||||
|
can_create,
|
||||||
|
can_edit,
|
||||||
|
can_delete_set,
|
||||||
|
can_delete,
|
||||||
|
can_view,
|
||||||
|
can_view_all,
|
||||||
|
can_change
|
||||||
|
)
|
||||||
|
|
||||||
|
from .serializers import MailingSerializer, MailingMemberSerializer
|
||||||
|
from .models import (
|
||||||
User,
|
User,
|
||||||
Ban,
|
Ban,
|
||||||
Whitelist,
|
Whitelist,
|
||||||
|
@ -66,7 +81,7 @@ from users.models import (
|
||||||
Club,
|
Club,
|
||||||
ListShell,
|
ListShell,
|
||||||
)
|
)
|
||||||
from users.forms import (
|
from .forms import (
|
||||||
BanForm,
|
BanForm,
|
||||||
WhitelistForm,
|
WhitelistForm,
|
||||||
DelSchoolForm,
|
DelSchoolForm,
|
||||||
|
@ -86,25 +101,7 @@ from users.forms import (
|
||||||
ClubAdminandMembersForm,
|
ClubAdminandMembersForm,
|
||||||
GroupForm
|
GroupForm
|
||||||
)
|
)
|
||||||
from cotisations.models import Facture
|
|
||||||
from machines.models import Machine
|
|
||||||
from preferences.models import OptionalUser, GeneralOption, AssoOption
|
|
||||||
|
|
||||||
from re2o.views import form
|
|
||||||
from re2o.utils import (
|
|
||||||
all_has_access,
|
|
||||||
SortTable,
|
|
||||||
re2o_paginator
|
|
||||||
)
|
|
||||||
from re2o.acl import (
|
|
||||||
can_create,
|
|
||||||
can_edit,
|
|
||||||
can_delete_set,
|
|
||||||
can_delete,
|
|
||||||
can_view,
|
|
||||||
can_view_all,
|
|
||||||
can_change
|
|
||||||
)
|
|
||||||
|
|
||||||
@can_create(Adherent)
|
@can_create(Adherent)
|
||||||
def new_user(request):
|
def new_user(request):
|
||||||
|
@ -121,9 +118,19 @@ def new_user(request):
|
||||||
pour l'initialisation du mot de passe a été envoyé" % user.pseudo)
|
pour l'initialisation du mot de passe a été envoyé" % user.pseudo)
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid':str(user.id)}
|
kwargs={'userid': str(user.id)}
|
||||||
))
|
))
|
||||||
return form({'userform': user,'GTU_sum_up':GTU_sum_up,'GTU':GTU,'showCGU':True, 'action_name':'Créer un utilisateur'}, 'users/user.html', request)
|
return form(
|
||||||
|
{
|
||||||
|
'userform': user,
|
||||||
|
'GTU_sum_up': GTU_sum_up,
|
||||||
|
'GTU': GTU,
|
||||||
|
'showCGU': True,
|
||||||
|
'action_name': 'Créer un utilisateur'
|
||||||
|
},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -140,26 +147,41 @@ def new_club(request):
|
||||||
pour l'initialisation du mot de passe a été envoyé" % club.pseudo)
|
pour l'initialisation du mot de passe a été envoyé" % club.pseudo)
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid':str(club.id)}
|
kwargs={'userid': str(club.id)}
|
||||||
))
|
))
|
||||||
return form({'userform': club, 'showCGU':False, 'action_name':'Créer un club'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': club, 'showCGU': False, 'action_name': 'Créer un club'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Club)
|
@can_edit(Club)
|
||||||
def edit_club_admin_members(request, club_instance, clubid):
|
def edit_club_admin_members(request, club_instance, **_kwargs):
|
||||||
"""Vue d'edition de la liste des users administrateurs et
|
"""Vue d'edition de la liste des users administrateurs et
|
||||||
membres d'un club"""
|
membres d'un club"""
|
||||||
club = ClubAdminandMembersForm(request.POST or None, instance=club_instance)
|
club = ClubAdminandMembersForm(
|
||||||
|
request.POST or None,
|
||||||
|
instance=club_instance
|
||||||
|
)
|
||||||
if club.is_valid():
|
if club.is_valid():
|
||||||
if club.changed_data:
|
if club.changed_data:
|
||||||
club.save()
|
club.save()
|
||||||
messages.success(request, "Le club a bien été modifié")
|
messages.success(request, "Le club a bien été modifié")
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid':str(club_instance.id)}
|
kwargs={'userid': str(club_instance.id)}
|
||||||
))
|
))
|
||||||
return form({'userform': club, 'showCGU':False, 'action_name':'Editer les admin et membres'}, 'users/user.html', request)
|
return form(
|
||||||
|
{
|
||||||
|
'userform': club,
|
||||||
|
'showCGU': False,
|
||||||
|
'action_name': 'Editer les admin et membres'
|
||||||
|
},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -169,26 +191,30 @@ def edit_info(request, user, userid):
|
||||||
si l'id est différent de request.user, vérifie la
|
si l'id est différent de request.user, vérifie la
|
||||||
possession du droit cableur """
|
possession du droit cableur """
|
||||||
if user.is_class_adherent:
|
if user.is_class_adherent:
|
||||||
user = AdherentForm(
|
user_form = AdherentForm(
|
||||||
request.POST or None,
|
request.POST or None,
|
||||||
instance=user.adherent,
|
instance=user.adherent,
|
||||||
user=request.user
|
user=request.user
|
||||||
)
|
)
|
||||||
elif user.is_class_club:
|
else:
|
||||||
user = ClubForm(
|
user_form = ClubForm(
|
||||||
request.POST or None,
|
request.POST or None,
|
||||||
instance=user.club,
|
instance=user.club,
|
||||||
user=request.user
|
user=request.user
|
||||||
)
|
)
|
||||||
if user.is_valid():
|
if user_form.is_valid():
|
||||||
if user.changed_data:
|
if user_form.changed_data:
|
||||||
user.save()
|
user_form.save()
|
||||||
messages.success(request, "L'user a bien été modifié")
|
messages.success(request, "L'user a bien été modifié")
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid':str(userid)}
|
kwargs={'userid': str(userid)}
|
||||||
))
|
))
|
||||||
return form({'userform': user, 'action_name': "Editer l'utilisateur"}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': user_form, 'action_name': "Editer l'utilisateur"},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -196,35 +222,44 @@ def edit_info(request, user, userid):
|
||||||
def state(request, user, userid):
|
def state(request, user, userid):
|
||||||
""" Changer l'etat actif/desactivé/archivé d'un user,
|
""" Changer l'etat actif/desactivé/archivé d'un user,
|
||||||
need droit bureau """
|
need droit bureau """
|
||||||
state = StateForm(request.POST or None, instance=user)
|
state_form = StateForm(request.POST or None, instance=user)
|
||||||
if state.is_valid():
|
if state_form.is_valid():
|
||||||
if state.changed_data:
|
if state_form.changed_data:
|
||||||
if state.cleaned_data['state'] == User.STATE_ARCHIVE:
|
if state_form.cleaned_data['state'] == User.STATE_ARCHIVE:
|
||||||
user.archive()
|
user.archive()
|
||||||
elif state.cleaned_data['state'] == User.STATE_ACTIVE:
|
elif state_form.cleaned_data['state'] == User.STATE_ACTIVE:
|
||||||
user.unarchive()
|
user.unarchive()
|
||||||
state.save()
|
state_form.save()
|
||||||
messages.success(request, "Etat changé avec succès")
|
messages.success(request, "Etat changé avec succès")
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid':str(userid)}
|
kwargs={'userid': str(userid)}
|
||||||
))
|
))
|
||||||
return form({'userform': state, 'action_name': "Editer l'état"}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': state_form, 'action_name': "Editer l'état"},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(User, 'groups')
|
@can_edit(User, 'groups')
|
||||||
def groups(request, user, userid):
|
def groups(request, user, userid):
|
||||||
group = GroupForm(request.POST or None, instance=user)
|
""" View to edit the groups of a user """
|
||||||
if group.is_valid():
|
group_form = GroupForm(request.POST or None, instance=user)
|
||||||
if group.changed_data:
|
if group_form.is_valid():
|
||||||
group.save()
|
if group_form.changed_data:
|
||||||
|
group_form.save()
|
||||||
messages.success(request, "Groupes changés avec succès")
|
messages.success(request, "Groupes changés avec succès")
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid':str(userid)}
|
kwargs={'userid': str(userid)}
|
||||||
))
|
))
|
||||||
return form({'userform': group, 'action_name':'Editer les groupes'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': group_form, 'action_name': 'Editer les groupes'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -239,15 +274,20 @@ def password(request, user, userid):
|
||||||
u_form.save()
|
u_form.save()
|
||||||
messages.success(request, "Le mot de passe a changé")
|
messages.success(request, "Le mot de passe a changé")
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid':str(user.id)}
|
kwargs={'userid': str(userid)}
|
||||||
))
|
))
|
||||||
return form({'userform': u_form, 'action_name':'Changer le mot de passe'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': u_form, 'action_name': 'Changer le mot de passe'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(User, 'groups')
|
@can_edit(User, 'groups')
|
||||||
def del_group(request, user, userid, listrightid):
|
def del_group(request, user, listrightid, **_kwargs):
|
||||||
|
""" View used to delete a group """
|
||||||
user.groups.remove(ListRight.objects.get(id=listrightid))
|
user.groups.remove(ListRight.objects.get(id=listrightid))
|
||||||
user.save()
|
user.save()
|
||||||
messages.success(request, "Droit supprimé à %s" % user)
|
messages.success(request, "Droit supprimé à %s" % user)
|
||||||
|
@ -268,14 +308,21 @@ def new_serviceuser(request):
|
||||||
"L'utilisateur %s a été crée" % user_object.pseudo
|
"L'utilisateur %s a été crée" % user_object.pseudo
|
||||||
)
|
)
|
||||||
return redirect(reverse('users:index-serviceusers'))
|
return redirect(reverse('users:index-serviceusers'))
|
||||||
return form({'userform': user, 'action_name':'Créer un serviceuser'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': user, 'action_name': 'Créer un serviceuser'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(ServiceUser)
|
@can_edit(ServiceUser)
|
||||||
def edit_serviceuser(request, serviceuser, serviceuserid):
|
def edit_serviceuser(request, serviceuser, **_kwargs):
|
||||||
""" Edit a ServiceUser """
|
""" Edit a ServiceUser """
|
||||||
serviceuser = EditServiceUserForm(request.POST or None, instance=serviceuser)
|
serviceuser = EditServiceUserForm(
|
||||||
|
request.POST or None,
|
||||||
|
instance=serviceuser
|
||||||
|
)
|
||||||
if serviceuser.is_valid():
|
if serviceuser.is_valid():
|
||||||
user_object = serviceuser.save(commit=False)
|
user_object = serviceuser.save(commit=False)
|
||||||
if serviceuser.cleaned_data['password']:
|
if serviceuser.cleaned_data['password']:
|
||||||
|
@ -284,12 +331,16 @@ def edit_serviceuser(request, serviceuser, serviceuserid):
|
||||||
user_object.save()
|
user_object.save()
|
||||||
messages.success(request, "L'user a bien été modifié")
|
messages.success(request, "L'user a bien été modifié")
|
||||||
return redirect(reverse('users:index-serviceusers'))
|
return redirect(reverse('users:index-serviceusers'))
|
||||||
return form({'userform': serviceuser, 'action_name':'Editer un serviceuser'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': serviceuser, 'action_name': 'Editer un serviceuser'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_delete(ServiceUser)
|
@can_delete(ServiceUser)
|
||||||
def del_serviceuser(request, serviceuser, serviceuserid):
|
def del_serviceuser(request, serviceuser, **_kwargs):
|
||||||
"""Suppression d'un ou plusieurs serviceusers"""
|
"""Suppression d'un ou plusieurs serviceusers"""
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
serviceuser.delete()
|
serviceuser.delete()
|
||||||
|
@ -312,22 +363,27 @@ def add_ban(request, user, userid):
|
||||||
ban_instance = Ban(user=user)
|
ban_instance = Ban(user=user)
|
||||||
ban = BanForm(request.POST or None, instance=ban_instance)
|
ban = BanForm(request.POST or None, instance=ban_instance)
|
||||||
if ban.is_valid():
|
if ban.is_valid():
|
||||||
_ban_object = ban.save()
|
ban.save()
|
||||||
messages.success(request, "Bannissement ajouté")
|
messages.success(request, "Bannissement ajouté")
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid':str(userid)}
|
kwargs={'userid': str(userid)}
|
||||||
))
|
))
|
||||||
if user.is_ban():
|
if user.is_ban():
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
"Attention, cet utilisateur a deja un bannissement actif"
|
"Attention, cet utilisateur a deja un bannissement actif"
|
||||||
)
|
)
|
||||||
return form({'userform': ban, 'action_name': 'Ajouter un ban'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': ban, 'action_name': 'Ajouter un ban'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Ban)
|
@can_edit(Ban)
|
||||||
def edit_ban(request, ban_instance, banid):
|
def edit_ban(request, ban_instance, **_kwargs):
|
||||||
""" Editer un bannissement, nécessite au moins le droit bofh
|
""" Editer un bannissement, nécessite au moins le droit bofh
|
||||||
(a fortiori bureau)
|
(a fortiori bureau)
|
||||||
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement"""
|
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement"""
|
||||||
|
@ -337,7 +393,11 @@ def edit_ban(request, ban_instance, banid):
|
||||||
ban.save()
|
ban.save()
|
||||||
messages.success(request, "Bannissement modifié")
|
messages.success(request, "Bannissement modifié")
|
||||||
return redirect(reverse('users:index'))
|
return redirect(reverse('users:index'))
|
||||||
return form({'userform': ban, 'action_name': 'Editer un ban'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': ban, 'action_name': 'Editer un ban'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -358,19 +418,23 @@ def add_whitelist(request, user, userid):
|
||||||
messages.success(request, "Accès à titre gracieux accordé")
|
messages.success(request, "Accès à titre gracieux accordé")
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid':str(userid)}
|
kwargs={'userid': str(userid)}
|
||||||
))
|
))
|
||||||
if user.is_whitelisted():
|
if user.is_whitelisted():
|
||||||
messages.error(
|
messages.error(
|
||||||
request,
|
request,
|
||||||
"Attention, cet utilisateur a deja un accès gracieux actif"
|
"Attention, cet utilisateur a deja un accès gracieux actif"
|
||||||
)
|
)
|
||||||
return form({'userform': whitelist, 'action_name': 'Ajouter une whitelist'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': whitelist, 'action_name': 'Ajouter une whitelist'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(Whitelist)
|
@can_edit(Whitelist)
|
||||||
def edit_whitelist(request, whitelist_instance, whitelistid):
|
def edit_whitelist(request, whitelist_instance, **_kwargs):
|
||||||
""" Editer un accès gracieux, temporaire ou permanent.
|
""" Editer un accès gracieux, temporaire ou permanent.
|
||||||
Need droit cableur
|
Need droit cableur
|
||||||
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement,
|
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement,
|
||||||
|
@ -384,7 +448,11 @@ def edit_whitelist(request, whitelist_instance, whitelistid):
|
||||||
whitelist.save()
|
whitelist.save()
|
||||||
messages.success(request, "Whitelist modifiée")
|
messages.success(request, "Whitelist modifiée")
|
||||||
return redirect(reverse('users:index'))
|
return redirect(reverse('users:index'))
|
||||||
return form({'userform': whitelist, 'action_name': 'Editer une whitelist'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': whitelist, 'action_name': 'Editer une whitelist'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -397,12 +465,16 @@ def add_school(request):
|
||||||
school.save()
|
school.save()
|
||||||
messages.success(request, "L'établissement a été ajouté")
|
messages.success(request, "L'établissement a été ajouté")
|
||||||
return redirect(reverse('users:index-school'))
|
return redirect(reverse('users:index-school'))
|
||||||
return form({'userform': school, 'action_name':'Ajouter'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': school, 'action_name': 'Ajouter'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(School)
|
@can_edit(School)
|
||||||
def edit_school(request, school_instance, schoolid):
|
def edit_school(request, school_instance, **_kwargs):
|
||||||
""" Editer un établissement d'enseignement à partir du schoolid dans
|
""" Editer un établissement d'enseignement à partir du schoolid dans
|
||||||
la base de donnée, need cableur"""
|
la base de donnée, need cableur"""
|
||||||
school = SchoolForm(request.POST or None, instance=school_instance)
|
school = SchoolForm(request.POST or None, instance=school_instance)
|
||||||
|
@ -411,7 +483,11 @@ def edit_school(request, school_instance, schoolid):
|
||||||
school.save()
|
school.save()
|
||||||
messages.success(request, "Établissement modifié")
|
messages.success(request, "Établissement modifié")
|
||||||
return redirect(reverse('users:index-school'))
|
return redirect(reverse('users:index-school'))
|
||||||
return form({'userform': school, 'action_name':'Editer'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': school, 'action_name': 'Editer'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -434,7 +510,11 @@ def del_school(request, instances):
|
||||||
"L'établissement %s est affecté à au moins un user, \
|
"L'établissement %s est affecté à au moins un user, \
|
||||||
vous ne pouvez pas le supprimer" % school_del)
|
vous ne pouvez pas le supprimer" % school_del)
|
||||||
return redirect(reverse('users:index-school'))
|
return redirect(reverse('users:index-school'))
|
||||||
return form({'userform': school, 'action_name': 'Supprimer'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': school, 'action_name': 'Supprimer'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -446,12 +526,16 @@ def add_shell(request):
|
||||||
shell.save()
|
shell.save()
|
||||||
messages.success(request, "Le shell a été ajouté")
|
messages.success(request, "Le shell a été ajouté")
|
||||||
return redirect(reverse('users:index-shell'))
|
return redirect(reverse('users:index-shell'))
|
||||||
return form({'userform': shell, 'action_name':'Ajouter'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': shell, 'action_name': 'Ajouter'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(ListShell)
|
@can_edit(ListShell)
|
||||||
def edit_shell(request, shell_instance, listshellid):
|
def edit_shell(request, shell_instance, **_kwargs):
|
||||||
""" Editer un shell à partir du listshellid"""
|
""" Editer un shell à partir du listshellid"""
|
||||||
shell = ShellForm(request.POST or None, instance=shell_instance)
|
shell = ShellForm(request.POST or None, instance=shell_instance)
|
||||||
if shell.is_valid():
|
if shell.is_valid():
|
||||||
|
@ -459,12 +543,16 @@ def edit_shell(request, shell_instance, listshellid):
|
||||||
shell.save()
|
shell.save()
|
||||||
messages.success(request, "Le shell a été modifié")
|
messages.success(request, "Le shell a été modifié")
|
||||||
return redirect(reverse('users:index-shell'))
|
return redirect(reverse('users:index-shell'))
|
||||||
return form({'userform': shell, 'action_name':'Editer'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': shell, 'action_name': 'Editer'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_delete(ListShell)
|
@can_delete(ListShell)
|
||||||
def del_shell(request, shell, listshellid):
|
def del_shell(request, shell, **_kwargs):
|
||||||
"""Destruction d'un shell"""
|
"""Destruction d'un shell"""
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
shell.delete()
|
shell.delete()
|
||||||
|
@ -487,12 +575,16 @@ def add_listright(request):
|
||||||
listright.save()
|
listright.save()
|
||||||
messages.success(request, "Le droit/groupe a été ajouté")
|
messages.success(request, "Le droit/groupe a été ajouté")
|
||||||
return redirect(reverse('users:index-listright'))
|
return redirect(reverse('users:index-listright'))
|
||||||
return form({'userform': listright, 'action_name': 'Ajouter'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': listright, 'action_name': 'Ajouter'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_edit(ListRight)
|
@can_edit(ListRight)
|
||||||
def edit_listright(request, listright_instance, listrightid):
|
def edit_listright(request, listright_instance, **_kwargs):
|
||||||
""" Editer un groupe/droit, necessite droit bureau,
|
""" Editer un groupe/droit, necessite droit bureau,
|
||||||
à partir du listright id """
|
à partir du listright id """
|
||||||
listright = ListRightForm(
|
listright = ListRightForm(
|
||||||
|
@ -504,7 +596,11 @@ def edit_listright(request, listright_instance, listrightid):
|
||||||
listright.save()
|
listright.save()
|
||||||
messages.success(request, "Droit modifié")
|
messages.success(request, "Droit modifié")
|
||||||
return redirect(reverse('users:index-listright'))
|
return redirect(reverse('users:index-listright'))
|
||||||
return form({'userform': listright, 'action_name': 'Editer'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': listright, 'action_name': 'Editer'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -525,7 +621,11 @@ def del_listright(request, instances):
|
||||||
"Le groupe %s est affecté à au moins un user, \
|
"Le groupe %s est affecté à au moins un user, \
|
||||||
vous ne pouvez pas le supprimer" % listright_del)
|
vous ne pouvez pas le supprimer" % listright_del)
|
||||||
return redirect(reverse('users:index-listright'))
|
return redirect(reverse('users:index-listright'))
|
||||||
return form({'userform': listright, 'action_name': 'Supprimer'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': listright, 'action_name': 'Supprimer'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -587,7 +687,11 @@ def index_clubs(request):
|
||||||
SortTable.USERS_INDEX
|
SortTable.USERS_INDEX
|
||||||
)
|
)
|
||||||
clubs_list = re2o_paginator(request, clubs_list, pagination_number)
|
clubs_list = re2o_paginator(request, clubs_list, pagination_number)
|
||||||
return render(request, 'users/index_clubs.html', {'clubs_list': clubs_list})
|
return render(
|
||||||
|
request,
|
||||||
|
'users/index_clubs.html',
|
||||||
|
{'clubs_list': clubs_list}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -688,13 +792,13 @@ def mon_profil(request):
|
||||||
""" Lien vers profil, renvoie request.id à la fonction """
|
""" Lien vers profil, renvoie request.id à la fonction """
|
||||||
return redirect(reverse(
|
return redirect(reverse(
|
||||||
'users:profil',
|
'users:profil',
|
||||||
kwargs={'userid':str(request.user.id)}
|
kwargs={'userid': str(request.user.id)}
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@can_view(User)
|
@can_view(User)
|
||||||
def profil(request, users, userid):
|
def profil(request, users, **_kwargs):
|
||||||
""" Affiche un profil, self or cableur, prend un userid en argument """
|
""" Affiche un profil, self or cableur, prend un userid en argument """
|
||||||
machines = Machine.objects.filter(user=users).select_related('user')\
|
machines = Machine.objects.filter(user=users).select_related('user')\
|
||||||
.prefetch_related('interface_set__domain__extension')\
|
.prefetch_related('interface_set__domain__extension')\
|
||||||
|
@ -707,7 +811,9 @@ def profil(request, users, userid):
|
||||||
request.GET.get('order'),
|
request.GET.get('order'),
|
||||||
SortTable.MACHINES_INDEX
|
SortTable.MACHINES_INDEX
|
||||||
)
|
)
|
||||||
pagination_large_number = GeneralOption.get_cached_value('pagination_large_number')
|
pagination_large_number = GeneralOption.get_cached_value(
|
||||||
|
'pagination_large_number'
|
||||||
|
)
|
||||||
machines = re2o_paginator(request, machines, pagination_large_number)
|
machines = re2o_paginator(request, machines, pagination_large_number)
|
||||||
factures = Facture.objects.filter(user=users)
|
factures = Facture.objects.filter(user=users)
|
||||||
factures = SortTable.sort(
|
factures = SortTable.sort(
|
||||||
|
@ -742,7 +848,7 @@ def profil(request, users, userid):
|
||||||
'ban_list': bans,
|
'ban_list': bans,
|
||||||
'white_list': whitelists,
|
'white_list': whitelists,
|
||||||
'user_solde': user_solde,
|
'user_solde': user_solde,
|
||||||
'allow_online_payment' : allow_online_payment,
|
'allow_online_payment': allow_online_payment,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -758,12 +864,20 @@ def reset_password(request):
|
||||||
)
|
)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
messages.error(request, "Cet utilisateur n'existe pas")
|
messages.error(request, "Cet utilisateur n'existe pas")
|
||||||
return form({'userform': userform, 'action_name': 'Réinitialiser'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': userform, 'action_name': 'Réinitialiser'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
user.reset_passwd_mail(request)
|
user.reset_passwd_mail(request)
|
||||||
messages.success(request, "Un mail pour l'initialisation du mot\
|
messages.success(request, "Un mail pour l'initialisation du mot\
|
||||||
de passe a été envoyé")
|
de passe a été envoyé")
|
||||||
redirect(reverse('index'))
|
redirect(reverse('index'))
|
||||||
return form({'userform': userform, 'action_name': 'Réinitialiser'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': userform, 'action_name': 'Réinitialiser'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def process(request, token):
|
def process(request, token):
|
||||||
|
@ -790,7 +904,11 @@ def process_passwd(request, req):
|
||||||
req.delete()
|
req.delete()
|
||||||
messages.success(request, "Le mot de passe a changé")
|
messages.success(request, "Le mot de passe a changé")
|
||||||
return redirect(reverse('index'))
|
return redirect(reverse('index'))
|
||||||
return form({'userform': u_form, 'action_name': 'Changer le mot de passe'}, 'users/user.html', request)
|
return form(
|
||||||
|
{'userform': u_form, 'action_name': 'Changer le mot de passe'},
|
||||||
|
'users/user.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class JSONResponse(HttpResponse):
|
class JSONResponse(HttpResponse):
|
||||||
|
@ -804,7 +922,7 @@ class JSONResponse(HttpResponse):
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
def ml_std_list(request):
|
def ml_std_list(_request):
|
||||||
""" API view sending all the available standard mailings"""
|
""" API view sending all the available standard mailings"""
|
||||||
return JSONResponse([
|
return JSONResponse([
|
||||||
{'name': 'adherents'}
|
{'name': 'adherents'}
|
||||||
|
@ -830,7 +948,7 @@ def ml_std_members(request, ml_name):
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('machines.serveur')
|
@permission_required('machines.serveur')
|
||||||
def ml_club_list(request):
|
def ml_club_list(_request):
|
||||||
""" API view sending all the available club mailings"""
|
""" API view sending all the available club mailings"""
|
||||||
clubs = Club.objects.filter(mailing=True).values('pseudo')
|
clubs = Club.objects.filter(mailing=True).values('pseudo')
|
||||||
seria = MailingSerializer(clubs, many=True)
|
seria = MailingSerializer(clubs, many=True)
|
||||||
|
@ -862,6 +980,9 @@ def ml_club_members(request, ml_name):
|
||||||
except Club.DoesNotExist:
|
except Club.DoesNotExist:
|
||||||
messages.error(request, "Cette mailing n'existe pas")
|
messages.error(request, "Cette mailing n'existe pas")
|
||||||
return redirect(reverse('index'))
|
return redirect(reverse('index'))
|
||||||
members = club.administrators.all().values('email').distinct() | club.members.all().values('email').distinct()
|
members = (
|
||||||
|
club.administrators.all().values('email').distinct() |
|
||||||
|
club.members.all().values('email').distinct()
|
||||||
|
)
|
||||||
seria = MailingMemberSerializer(members, many=True)
|
seria = MailingMemberSerializer(members, many=True)
|
||||||
return JSONResponse(seria.data)
|
return JSONResponse(seria.data)
|
||||||
|
|
Loading…
Reference in a new issue