diff --git a/api/serializers.py b/api/serializers.py new file mode 100644 index 00000000..1b02c577 --- /dev/null +++ b/api/serializers.py @@ -0,0 +1,360 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2018 Mael 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. + +""" +Serializers for the API app +""" + +from rest_framework import serializers +from users.models import Club, Adherent +from machines.models import ( + Interface, + IpType, + Extension, + IpList, + MachineType, + Domain, + Txt, + Mx, + Srv, + Service_link, + Ns, + OuverturePortList, + OuverturePort, + Ipv6List +) + + +class ServiceLinkSerializer(serializers.ModelSerializer): + name = serializers.CharField(source='service.service_type') + + class Meta: + model = Service_link + fields = ('name',) + + +class MailingSerializer(serializers.ModelSerializer): + name = serializers.CharField(source='pseudo') + + class Meta: + model = Club + fields = ('name',) + + +class MailingMemberSerializer(serializers.ModelSerializer): + class Meta: + model = Adherent + fields = ('email', 'name', 'surname', 'pseudo',) + + +class IpTypeField(serializers.RelatedField): + """Serialisation d'une iptype, renvoie son evaluation str""" + def to_representation(self, value): + return value.type + + +class IpListSerializer(serializers.ModelSerializer): + """Serialisation d'une iplist, ip_type etant une foreign_key, + on evalue sa methode str""" + ip_type = IpTypeField(read_only=True) + + class Meta: + model = IpList + fields = ('ipv4', 'ip_type') + + +class Ipv6ListSerializer(serializers.ModelSerializer): + class Meta: + model = Ipv6List + fields = ('ipv6', 'slaac_ip') + + +class InterfaceSerializer(serializers.ModelSerializer): + """Serialisation d'une interface, ipv4, domain et extension sont + des foreign_key, on les override et on les evalue avec des fonctions + get_...""" + ipv4 = IpListSerializer(read_only=True) + mac_address = serializers.SerializerMethodField('get_macaddress') + domain = serializers.SerializerMethodField('get_dns') + extension = serializers.SerializerMethodField('get_interface_extension') + + class Meta: + model = Interface + fields = ('ipv4', 'mac_address', 'domain', 'extension') + + def get_dns(self, obj): + return obj.domain.name + + def get_interface_extension(self, obj): + return obj.domain.extension.name + + def get_macaddress(self, obj): + return str(obj.mac_address) + + +class FullInterfaceSerializer(serializers.ModelSerializer): + """Serialisation complete d'une interface avec les ipv6 en plus""" + ipv4 = IpListSerializer(read_only=True) + ipv6 = Ipv6ListSerializer(read_only=True, many=True) + mac_address = serializers.SerializerMethodField('get_macaddress') + domain = serializers.SerializerMethodField('get_dns') + extension = serializers.SerializerMethodField('get_interface_extension') + + class Meta: + model = Interface + fields = ('ipv4', 'ipv6', 'mac_address', 'domain', 'extension') + + def get_dns(self, obj): + return obj.domain.name + + def get_interface_extension(self, obj): + return obj.domain.extension.name + + def get_macaddress(self, obj): + return str(obj.mac_address) + + +class ExtensionNameField(serializers.RelatedField): + """Evaluation str d'un objet extension (.example.org)""" + def to_representation(self, value): + return value.name + + +class TypeSerializer(serializers.ModelSerializer): + """Serialisation d'un iptype : extension et la liste des + ouvertures de port son evalués en get_... etant des + foreign_key ou des relations manytomany""" + extension = ExtensionNameField(read_only=True) + ouverture_ports_tcp_in = serializers\ + .SerializerMethodField('get_port_policy_input_tcp') + ouverture_ports_tcp_out = serializers\ + .SerializerMethodField('get_port_policy_output_tcp') + ouverture_ports_udp_in = serializers\ + .SerializerMethodField('get_port_policy_input_udp') + ouverture_ports_udp_out = serializers\ + .SerializerMethodField('get_port_policy_output_udp') + + class Meta: + model = IpType + fields = ('type', 'extension', 'domaine_ip_start', 'domaine_ip_stop', + 'prefix_v6', + 'ouverture_ports_tcp_in', 'ouverture_ports_tcp_out', + 'ouverture_ports_udp_in', 'ouverture_ports_udp_out',) + + def get_port_policy(self, obj, protocole, io): + if obj.ouverture_ports is None: + return [] + return map( + str, + obj.ouverture_ports.ouvertureport_set.filter( + protocole=protocole + ).filter(io=io) + ) + + def get_port_policy_input_tcp(self, obj): + """Renvoie la liste des ports ouverts en entrée tcp""" + return self.get_port_policy(obj, OuverturePort.TCP, OuverturePort.IN) + + def get_port_policy_output_tcp(self, obj): + """Renvoie la liste des ports ouverts en sortie tcp""" + return self.get_port_policy(obj, OuverturePort.TCP, OuverturePort.OUT) + + def get_port_policy_input_udp(self, obj): + """Renvoie la liste des ports ouverts en entrée udp""" + return self.get_port_policy(obj, OuverturePort.UDP, OuverturePort.IN) + + def get_port_policy_output_udp(self, obj): + """Renvoie la liste des ports ouverts en sortie udp""" + return self.get_port_policy(obj, OuverturePort.UDP, OuverturePort.OUT) + + +class ExtensionSerializer(serializers.ModelSerializer): + """Serialisation d'une extension : origin_ip et la zone sont + des foreign_key donc evalués en get_...""" + origin = serializers.SerializerMethodField('get_origin_ip') + zone_entry = serializers.SerializerMethodField('get_zone_name') + soa = serializers.SerializerMethodField('get_soa_data') + + class Meta: + model = Extension + fields = ('name', 'origin', 'origin_v6', 'zone_entry', 'soa') + + def get_origin_ip(self, obj): + return obj.origin.ipv4 + + def get_zone_name(self, obj): + return str(obj.dns_entry) + + def get_soa_data(self, obj): + return { 'mail': obj.soa.dns_soa_mail, 'param': obj.soa.dns_soa_param } + + +class MxSerializer(serializers.ModelSerializer): + """Serialisation d'un MX, evaluation du nom, de la zone + et du serveur cible, etant des foreign_key""" + name = serializers.SerializerMethodField('get_entry_name') + zone = serializers.SerializerMethodField('get_zone_name') + mx_entry = serializers.SerializerMethodField('get_mx_name') + + class Meta: + model = Mx + fields = ('zone', 'priority', 'name', 'mx_entry') + + def get_entry_name(self, obj): + return str(obj.name) + + def get_zone_name(self, obj): + return obj.zone.name + + def get_mx_name(self, obj): + return str(obj.dns_entry) + + +class TxtSerializer(serializers.ModelSerializer): + """Serialisation d'un txt : zone cible et l'entrée txt + sont evaluées à part""" + zone = serializers.SerializerMethodField('get_zone_name') + txt_entry = serializers.SerializerMethodField('get_txt_name') + + class Meta: + model = Txt + fields = ('zone', 'txt_entry', 'field1', 'field2') + + def get_zone_name(self, obj): + return str(obj.zone.name) + + def get_txt_name(self, obj): + return str(obj.dns_entry) + + +class SrvSerializer(serializers.ModelSerializer): + """Serialisation d'un srv : zone cible et l'entrée txt""" + extension = serializers.SerializerMethodField('get_extension_name') + srv_entry = serializers.SerializerMethodField('get_srv_name') + + class Meta: + model = Srv + fields = ( + 'service', + 'protocole', + 'extension', + 'ttl', + 'priority', + 'weight', + 'port', + 'target', + 'srv_entry' + ) + + def get_extension_name(self, obj): + return str(obj.extension.name) + + def get_srv_name(self, obj): + return str(obj.dns_entry) + + +class NsSerializer(serializers.ModelSerializer): + """Serialisation d'un NS : la zone, l'entrée ns complète et le serveur + ns sont évalués à part""" + zone = serializers.SerializerMethodField('get_zone_name') + ns = serializers.SerializerMethodField('get_domain_name') + ns_entry = serializers.SerializerMethodField('get_text_name') + + class Meta: + model = Ns + fields = ('zone', 'ns', 'ns_entry') + + def get_zone_name(self, obj): + return obj.zone.name + + def get_domain_name(self, obj): + return str(obj.ns) + + def get_text_name(self, obj): + return str(obj.dns_entry) + + +class DomainSerializer(serializers.ModelSerializer): + """Serialisation d'un domain, extension, cname sont des foreign_key, + et l'entrée complète, sont évalués à part""" + extension = serializers.SerializerMethodField('get_zone_name') + cname = serializers.SerializerMethodField('get_alias_name') + cname_entry = serializers.SerializerMethodField('get_cname_name') + + class Meta: + model = Domain + fields = ('name', 'extension', 'cname', 'cname_entry') + + def get_zone_name(self, obj): + return obj.extension.name + + def get_alias_name(self, obj): + return str(obj.cname) + + def get_cname_name(self, obj): + return str(obj.dns_entry) + + +class ServicesSerializer(serializers.ModelSerializer): + """Evaluation d'un Service, et serialisation""" + server = serializers.SerializerMethodField('get_server_name') + service = serializers.SerializerMethodField('get_service_name') + need_regen = serializers.SerializerMethodField('get_regen_status') + + class Meta: + model = Service_link + fields = ('server', 'service', 'need_regen') + + def get_server_name(self, obj): + return str(obj.server.domain.name) + + def get_service_name(self, obj): + return str(obj.service) + + def get_regen_status(self, obj): + return obj.need_regen() + + +class OuverturePortsSerializer(serializers.Serializer): + """Serialisation de l'ouverture des ports""" + ipv4 = serializers.SerializerMethodField() + ipv6 = serializers.SerializerMethodField() + + def get_ipv4(): + return {i.ipv4.ipv4: + { + "tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()], + "tcp_out":[j.tcp_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 + } + + def get_ipv6(): + return {i.ipv6: + { + "tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()], + "tcp_out":[j.tcp_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 + } diff --git a/api/tests.py b/api/tests.py new file mode 100644 index 00000000..21fa6d24 --- /dev/null +++ b/api/tests.py @@ -0,0 +1,25 @@ +# 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 django.test import TestCase + +# Create your tests here. diff --git a/api/urls.py b/api/urls.py new file mode 100644 index 00000000..584dd600 --- /dev/null +++ b/api/urls.py @@ -0,0 +1,59 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2018 Mael 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. +"""api.urls + +Urls de l'api, pointant vers les fonctions de views +""" + +from __future__ import unicode_literals + +from django.conf.urls import url + +from . import views + + +urlpatterns = [ + # Services + url(r'^services/$', views.services), + url(r'^services/(?P\w+)/(?P\w+)/regen/$', views.services_server_service_regen), + url(r'^services/(?P\w+)/$', views.services_server), + + # DNS + url(r'^dns/mac-ip-dns/$', views.dns_mac_ip_dns), + url(r'^dns/alias/$', views.dns_alias), + url(r'^dns/corresp/$', views.dns_corresp), + url(r'^dns/mx/$', views.dns_mx), + url(r'^dns/ns/$', views.dns_ns), + url(r'^dns/txt/$', views.dns_txt), + url(r'^dns/srv/$', views.dns_srv), + url(r'^dns/zones/$', views.dns_zones), + + # Firewall + url(r'^firewall/ouverture_ports/$', views.firewall_ouverture_ports), + + # DHCP + url(r'^dhcp/mac-ip/$', views.dhcp_mac_ip), + + # Mailings + url(r'^mailing/standard/$', views.mailing_standard), + url(r'^mailing/standard/(?P\w+)/members/$', views.mailing_standard_ml_members), + url(r'^mailing/club/$', views.mailing_club), + url(r'^mailing/club/(?P\w+)/members/$', views.mailing_club_ml_members), +] diff --git a/api/utils.py b/api/utils.py new file mode 100644 index 00000000..024c9a39 --- /dev/null +++ b/api/utils.py @@ -0,0 +1,114 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2018 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. + +"""api.utils. + +Set of various and usefull functions for the API app +""" + +from rest_framework.renderers import JSONRenderer +from django.http import HttpResponse + +class JSONResponse(HttpResponse): + """A JSON response that can be send as an HTTP response. + Usefull in case of REST API. + """ + + def __init__(self, data, **kwargs): + """Initialisz a JSONResponse object. + + Args: + data: the data to render as JSON (often made of lists, dicts, + strings, boolean and numbers). See `JSONRenderer.render(data)` for + further details. + + Creates: + An HTTPResponse containing the data in JSON format. + """ + + content = JSONRenderer().render(data) + kwargs['content_type'] = 'application/json' + super(JSONResponse, self).__init__(content, **kwargs) + + +class JSONError(JSONResponse): + """A JSON response when the request failed. + """ + + def __init__(self, error_msg, data=None, **kwargs): + """Initialise a JSONError object. + + Args: + error_msg: A message explaining where the error is. + data: An optional field for further data to send along. + + Creates: + A JSONResponse containing a field `status` set to `error` and a field + `reason` containing `error_msg`. If `data` argument has been given, + a field `data` containing it is added to the JSON response. + """ + + response = { + 'status' : 'error', + 'reason' : error_msg + } + if data is not None: + response['data'] = data + super(JSONError, self).__init__(response, **kwargs) + + +class JSONSuccess(JSONResponse): + """A JSON response when the request suceeded. + """ + + def __init__(self, data=None, **kwargs): + """Initialise a JSONSucess object. + + Args: + error_msg: A message explaining where the error is. + data: An optional field for further data to send along. + + Creates: + A JSONResponse containing a field `status` set to `sucess`. If `data` + argument has been given, a field `data` containing it is added to the + JSON response. + """ + + response = { + 'status' : 'success', + } + if data is not None: + response['data'] = data + super(JSONSuccess, self).__init__(response, **kwargs) + + +def accept_method(methods): + """Decorator to set a list of accepted request method. + Check if the method used is accepted. If not, send a NotAllowed response. + """ + def decorator(view): + def wrapper(request, *args, **kwargs): + if request.method in methods: + return view(request, *args, **kwargs) + else: + return JSONError('Invalid request method. Request methods authorize are '+str(methods)) + return view(request, *args, **kwargs) + return wrapper + return decorator diff --git a/api/views.py b/api/views.py new file mode 100644 index 00000000..cc2237de --- /dev/null +++ b/api/views.py @@ -0,0 +1,464 @@ +# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2018 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. + +"""api.views + +The views for the API app. They should all return JSON data and not fallback on +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.views.decorators.csrf import csrf_exempt + +from re2o.utils import all_has_access, all_active_assigned_interfaces + +from users.models import Club +from machines.models import (Service_link, Service, Interface, Domain, + OuverturePortList) + +from .serializers import * +from .utils import JSONError, JSONSuccess, accept_method + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def services(request): + """The list of the different services and servers couples + + Return: + GET: + A JSONSuccess response with a field `data` containing: + * a list of dictionnaries (one for each service-server couple) containing: + * a field `server`: the server name + * a field `service`: the service name + * a field `need_regen`: does the service need a regeneration ? + """ + service_link = Service_link.objects.all().select_related('server__domain').select_related('service') + seria = ServicesSerializer(service_link, many=True) + return JSONSuccess(seria.data) + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET', 'POST']) +def services_server_service_regen(request, server_name, service_name): + """The status of a particular service linked to a particular server. + Mark the service as regenerated if POST used. + + Returns: + GET: + A JSONSucess response with a field `data` containing: + * a field `need_regen`: does the service need a regeneration ? + + POST: + An empty JSONSuccess response. + """ + query = Service_link.objects.filter( + service__in=Service.objects.filter(service_type=service_name), + server__in=Interface.objects.filter( + domain__in=Domain.objects.filter(name=server_name) + ) + ) + if not query: + return JSONError("This service is not active for this server") + + service = query.first() + if request.method == 'GET': + return JSONSuccess({'need_regen': service.need_regen()}) + else: + service.done_regen() + return JSONSuccess() + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def services_server(request, server_name): + """The list of services attached to a specific server + + Returns: + GET: + A JSONSuccess response with a field `data` containing: + * a list of dictionnaries (one for each service) containing: + * a field `name`: the name of a service + """ + query = Service_link.objects.filter( + server__in=Interface.objects.filter( + domain__in=Domain.objects.filter(name=server_name) + ) + ) + if not query: + return JSONError("This service is not active for this server") + + services = query.all() + seria = ServiceLinkSerializer(services, many=True) + return JSONSuccess(seria.data) + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def dns_mac_ip_dns(request): + """The list of all active interfaces with all the associated infos + (MAC, IP, IpType, DNS name and associated zone extension) + + Returns: + GET: + A JSON Success response with a field `data` containing: + * a list of dictionnaries (one for each interface) containing: + * a field `ipv4` containing: + * a field `ipv4`: the ip for this interface + * a field `ip_type`: the name of the IpType of this interface + * a field `ipv6` containing `null` if ipv6 is deactivated else: + * a field `ipv6`: the ip for 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 `domain`: the DNS name for this interface + * a field `extension`: the extension for the DNS zone of this interface + """ + interfaces = all_active_assigned_interfaces(full=True) + seria = FullInterfaceSerializer(interfaces, many=True) + return JSONSuccess(seria.data) + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def dns_alias(request): + """The list of all the alias used and the DNS info associated + + Returns: + GET: + A JSON Success response with a field `data` containing: + * a list of dictionnaries (one for each alias) containing: + * a field `name`: the alias used + * a field `cname`: the target of the alias (real name of the 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') + seria = DomainSerializer(alias, many=True) + return JSONSuccess(seria.data) + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def dns_corresp(request): + """The list of the IpTypes possible with the infos about each + + Returns: + GET: + A JSON Success response with a field `data` containing: + * a list of dictionnaries (one for each IpType) containing: + * a field `type`: the name of the type + * a field `extension`: the DNS extension associated + * 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 `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_out`: the policy for TCP OUT 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 + """ + ip_type = IpType.objects.all().select_related('extension') + seria = TypeSerializer(ip_type, many=True) + return JSONSuccess(seria.data) + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def dns_mx(request): + """The list of MX record to add to the DNS + + Returns: + GET: + A JSON Success response with a field `data` containing: + * a list of dictionnaries (one for each MX record) containing: + * a field `zone`: the extension for the concerned zone + * a field `priority`: the priority to use + * a field `name`: the name of the target + * 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') + seria = MxSerializer(mx, many=True) + return JSONSuccess(seria.data) + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def dns_ns(request): + """The list of NS record to add to the DNS + + Returns: + GET: + A JSON Success response with a field `data` containing: + * a list of dictionnaries (one for each NS record) containing: + * a field `zone`: the extension for the concerned zone + * 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 + """ + 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) + return JSONSuccess(seria.data) + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def dns_txt(request): + """The list of TXT record to add to the DNS + + Returns: + GET: + A JSON Success response with a field `data` containing: + * a list of dictionnaries (one for each TXT record) containing: + * a field `zone`: the extension for the concerned zone + * a field `field1`: the first field in the record (target) + * 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 + """ + txt = Txt.objects.all().select_related('zone') + seria = TxtSerializer(txt, many=True) + return JSONSuccess(seria.data) + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def dns_srv(request): + """The list of SRV record to add to the DNS + + Returns: + GET: + A JSON Success response with a field `data` containing: + * a list of dictionnaries (one for each SRV record) containing: + * a field `extension`: the extension for the concerned zone + * a field `service`: the name of the service concerned + * a field `protocole`: the name of the protocol to use + * a field `ttl`: the Time To Live to use + * a field `priority`: the priority for this service + * a field `weight`: the weight for same priority entries + * a field `port`: the port targeted + * 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 + """ + srv = Srv.objects.all().select_related('extension').select_related('target__extension') + seria = SrvSerializer(srv, many=True) + return JSONSuccess(seria.data) + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def dns_zones(request): + """The list of the zones managed + + Returns: + GET: + A JSON Success response with a field `data` containing: + * a list of dictionnaries (one for each zone) containing: + * a field `name`: the extension for 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 `soa` containing: + * a field `mail` containing the mail to contact in case of problem with 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') + seria = ExtensionSerializer(zones, many=True) + return JSONSuccess(seria.data) + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def firewall_ouverture_ports(request): + """The list of the ports authorized to be openned by the firewall + + Returns: + GET: + A JSONSuccess response with a `data` field containing: + * a field `ipv4` containing: + * a field `tcp_in` containing: + * a list of port number where ipv4 tcp in should be ok + * a field `tcp_out` containing: + * a list of port number where ipv4 tcp ou should be ok + * a field `udp_in` containing: + * a list of port number where ipv4 udp in should be ok + * a field `udp_out` containing: + * a list of port number where ipv4 udp out should be ok + * a field `ipv6` containing: + * a field `tcp_in` containing: + * a list of port number where ipv6 tcp in should be ok + * a field `tcp_out` containing: + * a list of port number where ipv6 tcp ou should be ok + * a field `udp_in` containing: + * a list of port number where ipv6 udp in should be ok + * a field `udp_out` containing: + * 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'): + pl = { + "tcp_in":set(map(str,o.ouvertureport_set.filter(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): + if i.may_have_port_open(): + d = r['ipv4'].get(i.ipv4.ipv4, {}) + d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"]) + d["tcp_out"] = d.get("tcp_out",set()).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 + if i.ipv6(): + for ipv6 in i.ipv6(): + d = r['ipv6'].get(ipv6.ipv6, {}) + d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"]) + d["tcp_out"] = d.get("tcp_out",set()).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 + return JSONSuccess(r) + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def dhcp_mac_ip(request): + """The list of all active interfaces with all the associated infos + (MAC, IP, IpType, DNS name and associated zone extension) + + Returns: + GET: + A JSON Success response with a field `data` containing: + * a list of dictionnaries (one for each interface) containing: + * a field `ipv4` containing: + * a field `ipv4`: the ip for 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 `domain`: the DNS name for this interface + * a field `extension`: the extension for the DNS zone of this interface + """ + interfaces = all_active_assigned_interfaces() + seria = InterfaceSerializer(interfaces, many=True) + return JSONSuccess(seria.data) + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def mailing_standard(request): + """All the available standard mailings. + + Returns: + GET: + A JSONSucess response with a field `data` containing: + * a list of dictionnaries (one for each mailing) containing: + * a field `name`: the name of a mailing + """ + return JSONSuccess([ + {'name': 'adherents'} + ]) + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def mailing_standard_ml_members(request): + """All the members of a specific standard mailing + + Returns: + GET: + A JSONSucess response with a field `data` containing: + * a list if dictionnaries (one for each member) containing: + * a field `email`: the email of the member + * a field `name`: the name of the member + * a field `surname`: the surname of the member + * a field `pseudo`: the pseudo of the member + """ + # All with active connextion + if ml_name == 'adherents': + members = all_has_access().values('email').distinct() + # Unknown mailing + else: + return JSONError("This mailing does not exist") + seria = MailingMemberSerializer(members, many=True) + return JSONSuccess(seria.data) + + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def mailing_club(request): + """All the available club mailings. + + Returns: + GET: + A JSONSucess response with a field `data` containing: + * a list of dictionnaries (one for each mailing) containing: + * a field `name` indicating the name of a mailing + """ + clubs = Club.objects.filter(mailing=True).values('pseudo') + seria = MailingSerializer(clubs, many=True) + return JSONSuccess(seria.data) + +@csrf_exempt +@login_required +@permission_required('machines.serveur') +@accept_method(['GET']) +def mailing_club_ml_members(request): + """All the members of a specific club mailing + + Returns: + GET: + A JSONSucess response with a field `data` containing: + * a list if dictionnaries (one for each member) containing: + * a field `email`: the email of the member + * a field `name`: the name of the member + * a field `surname`: the surname of the member + * a field `pseudo`: the pseudo of the member + """ + try: + club = Club.objects.get(mailing=True, pseudo=ml_name) + except Club.DoesNotExist: + return JSONError("This mailing does not exist") + members = club.administrators.all().values('email').distinct() + seria = MailingMemberSerializer(members, many=True) + return JSONSuccess(seria.data) diff --git a/re2o/settings.py b/re2o/settings.py index f88bd266..d1c27192 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -76,7 +76,8 @@ INSTALLED_APPS = ( 'preferences', 'logs', 'rest_framework', - 'reversion' + 'reversion', + 'api' ) + OPTIONNAL_APPS MIDDLEWARE_CLASSES = ( diff --git a/re2o/urls.py b/re2o/urls.py index 48a7ea98..52eafaab 100644 --- a/re2o/urls.py +++ b/re2o/urls.py @@ -27,12 +27,18 @@ The `urlpatterns` list routes URLs to views. For more information please see: Examples: Function views 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') + 2. Add a URL to urlpatterns: url(r'^$', views.home) + 3. Optional: Add a custom name for this URL: + url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view()) + 3. Optional: Add a custom name for this URL: + url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) + 2. Optional: Add a custom namespace for all URL using this urlpatterns: + url(r'^blog/', include('blog.urls'), namespace='blog') """ from __future__ import unicode_literals @@ -61,5 +67,6 @@ urlpatterns = [ r'^preferences/', include('preferences.urls', namespace='preferences') ), + url(r'^api/', include('api.urls', namespace='api')), ]