From 7a26aadaa9c231a9b0b9381730c4e2c22c2ddc56 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Tue, 4 Dec 2018 14:29:47 +0100 Subject: [PATCH 01/12] fix migrations. --- preferences/migrations/0055_generaloption_main_site_url.py | 2 +- users/forms.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/preferences/migrations/0055_generaloption_main_site_url.py b/preferences/migrations/0055_generaloption_main_site_url.py index 71ea9852..655c0b07 100644 --- a/preferences/migrations/0055_generaloption_main_site_url.py +++ b/preferences/migrations/0055_generaloption_main_site_url.py @@ -8,7 +8,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('preferences', '0054_merge_20181025_1258'), + ('preferences', '0053_optionaluser_self_change_room'), ] operations = [ diff --git a/users/forms.py b/users/forms.py index b96e3ad3..70d8798b 100644 --- a/users/forms.py +++ b/users/forms.py @@ -384,8 +384,8 @@ class AdherentCreationForm(AdherentForm): # Checkbox for GTU gtu_check = forms.BooleanField(required=True) - gtu_check.label = mark_safe("{} {}{}".format( - _("I commit to accept the"), GeneralOption.get_cached_value('GTU'), _("General Terms of Use"), _("."))) + #gtu_check.label = mark_safe("{} {}{}".format( + # _("I commit to accept the"), GeneralOption.get_cached_value('GTU'), _("General Terms of Use"), _("."))) def __init__(self, *args, **kwargs): super(AdherentCreationForm, self).__init__(*args, **kwargs) From a773f5d717662754d167b6028f1abc2206d397ed Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Sun, 2 Sep 2018 16:53:21 +0200 Subject: [PATCH 02/12] Pre-subscription VLAN --- freeradius_utils/auth.py | 17 ++-- .../migrations/0052_auto_20181013_1629.py | 91 ++++++++++++++++++ preferences/models.py | 85 +++++++++++++++- .../preferences/aff_radiusoptions.html | 96 +++++++++++++++++++ .../preferences/display_preferences.html | 55 ++++++----- preferences/urls.py | 5 + preferences/views.py | 7 +- 7 files changed, 324 insertions(+), 32 deletions(-) create mode 100644 preferences/migrations/0052_auto_20181013_1629.py create mode 100644 preferences/templates/preferences/aff_radiusoptions.html diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index ab10d457..9bd56015 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -63,6 +63,7 @@ from preferences.models import OptionalTopologie options, created = OptionalTopologie.objects.get_or_create() VLAN_NOK = options.vlan_decision_nok.vlan_id VLAN_OK = options.vlan_decision_ok.vlan_id +VLAN_NON_MEMBER = options.vlan_non_member.vlan_id RADIUS_POLICY = options.radius_general_policy #: Serveur radius de test (pas la prod) @@ -338,14 +339,15 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, - mode strict : - pas de chambre associée : VLAN_NOK - pas d'utilisateur dans la chambre : VLAN_NOK - - cotisation non à jour : VLAN_NOK + - cotisation non à jour : VLAN_NON_MEMBER - sinon passe à common (ci-dessous) - mode common : - interface connue (macaddress): - - utilisateur proprio non cotisant ou banni : VLAN_NOK + - utilisateur proprio non cotisant : VLAN_NON_MEMBER + - utilisateur proprio banni : VLAN_NOK - user à jour : VLAN_OK - interface inconnue : - - register mac désactivé : VLAN_NOK + - register mac désactivé : VLAN_NON_MEMBER - register mac activé -> redirection vers webauth """ # Get port from switch and port number @@ -414,8 +416,10 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, if not room_user: return (sw_name, room, u'Chambre non cotisante -> Web redirect', None, False) for user in room_user: - if not user.has_access(): + if user.is_ban() or user.state != User.STATE_ACTIVE: return (sw_name, room, u'Chambre resident desactive -> Web redirect', None, False) + elif not (user.is_connected() or user.is_whitelisted()): + return (sw_name, room, u'Utilisateur non cotisant', VLAN_NON_MEMBER) # else: user OK, on passe à la verif MAC # Si on fait de l'auth par mac, on cherche l'interface via sa mac dans la bdd @@ -431,7 +435,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, # On essaye de register la mac, si l'autocapture a été activée # Sinon on rejette sur vlan_nok if not nas_type.autocapture_mac: - return (sw_name, "", u'Machine inconnue', VLAN_NOK, True) + return (sw_name, "", u'Machine inconnue', VLAN_NON_MEMBER) # On rejette pour basculer sur du webauth else: return (sw_name, room, u'Machine Inconnue -> Web redirect', None, False) @@ -445,8 +449,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, return (sw_name, room, u'Machine non active / adherent non cotisant', - VLAN_NOK, - True) + VLAN_NON_MEMBER) ## Si on choisi de placer les machines sur le vlan correspondant à leur type : if RADIUS_POLICY == 'MACHINE': DECISION_VLAN = interface.type.ip_type.vlan.vlan_id diff --git a/preferences/migrations/0052_auto_20181013_1629.py b/preferences/migrations/0052_auto_20181013_1629.py new file mode 100644 index 00000000..70498536 --- /dev/null +++ b/preferences/migrations/0052_auto_20181013_1629.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-10-13 14:29 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import re2o.mixins + + +def create_radius_policy(apps, schema_editor): + OptionalTopologie = apps.get_model('preferences', 'OptionalTopologie') + RadiusOption = apps.get_model('preferences', 'RadiusOption') + RadiusPolicy = apps.get_model('preferences', 'RadiusPolicy') + + option,_ = OptionalTopologie.objects.get_or_create() + + radius_option = RadiusOption() + radius_option.radius_general_policy = option.radius_general_policy + radius_option.unknown_machine = RadiusPolicy.objects.create() + radius_option.unknown_port = RadiusPolicy.objects.create() + radius_option.unknown_room = RadiusPolicy.objects.create() + radius_option.non_member = RadiusPolicy.objects.create() + radius_option.banned = RadiusPolicy.objects.create() + radius_option.vlan_decision_ok = option.vlan_decision_ok + + radius_option.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0095_auto_20180919_2225'), + ('preferences', '0051_auto_20180919_2225'), + ] + + operations = [ + migrations.CreateModel( + name='RadiusOption', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('radius_general_policy', models.CharField(choices=[('MACHINE', "On the IP range's VLAN of the machine"), ('DEFINED', "Preset in 'VLAN for machines accepted by RADIUS'")], default='DEFINED', max_length=32)), + ], + options={ + 'verbose_name': 'radius policies', + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name='RadiusPolicy', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('policy', models.CharField(choices=[('REJECT', 'Reject the machine'), ('SET_VLAN', 'Place the machine on the VLAN')], default='REJECT', max_length=32)), + ('vlan', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.Vlan')), + ], + options={ + 'verbose_name': 'radius policy', + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.AddField( + model_name='radiusoption', + name='non_member', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='non_member_option', to='preferences.RadiusPolicy', verbose_name='Policy non member users.'), + ), + migrations.AddField( + model_name='radiusoption', + name='unknown_machine', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='unknown_machine_option', to='preferences.RadiusPolicy', verbose_name='Policy for unknown machines'), + ), + migrations.AddField( + model_name='radiusoption', + name='unknown_port', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='unknown_port_option', to='preferences.RadiusPolicy', verbose_name='Policy for unknown machines'), + ), + migrations.AddField( + model_name='radiusoption', + name='unknown_room', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='unknown_room_option', to='preferences.RadiusPolicy', verbose_name='Policy for machine connecting from unregistered room (relevant on ports with STRICT radius mode)'), + ), + migrations.AddField( + model_name='radiusoption', + name='banned', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='banned_option', to='preferences.RadiusPolicy', verbose_name='Policy for banned users.'), + ), + migrations.AddField( + model_name='radiusoption', + name='vlan_decision_ok', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlan_ok_option', to='machines.Vlan'), + ), + migrations.RunPython(create_radius_policy), + ] diff --git a/preferences/models.py b/preferences/models.py index 43ff7580..f3f5527e 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -218,6 +218,7 @@ class OptionalTopologie(AclMixin, PreferencesModel): blank=True, null=True ) + switchs_web_management = models.BooleanField( default=False, help_text="Web management, activé si provision automatique" @@ -309,7 +310,6 @@ class OptionalTopologie(AclMixin, PreferencesModel): """Return true if all settings are ok : switchs on automatic provision, ip_type""" return bool(self.provisioned_switchs and self.switchs_ip_type and SwitchManagementCred.objects.filter(default_switch=True).exists() and self.switchs_management_interface_ip and bool(self.switchs_provision != 'sftp' or self.switchs_management_sftp_creds)) - class Meta: permissions = ( ("view_optionaltopologie", _("Can view the topology options")), @@ -588,3 +588,86 @@ class MailMessageOption(AclMixin, models.Model): ) verbose_name = _("email message options") + +class RadiusPolicy(AclMixin, models.Model): + class Meta: + verbose_name = _('radius policy') + + REJECT = 'REJECT' + SET_VLAN = 'SET_VLAN' + CHOICE_POLICY = ( + (REJECT, _('Reject the machine')), + (SET_VLAN, _('Place the machine on the VLAN')) + ) + + policy = models.CharField( + max_length=32, + choices=CHOICE_POLICY, + default=REJECT + ) + + vlan = models.ForeignKey( + 'machines.Vlan', + on_delete=models.PROTECT, + blank=True, + null=True + ) + + +class RadiusOption(AclMixin, models.Model): + class Meta: + verbose_name = _("radius policies") + + MACHINE = 'MACHINE' + DEFINED = 'DEFINED' + CHOICE_RADIUS = ( + (MACHINE, _("On the IP range's VLAN of the machine")), + (DEFINED, _("Preset in 'VLAN for machines accepted by RADIUS'")), + ) + radius_general_policy = models.CharField( + max_length=32, + choices=CHOICE_RADIUS, + default='DEFINED' + ) + unknown_machine = models.ForeignKey( + RadiusPolicy, + on_delete=models.PROTECT, + verbose_name=_("Policy for unknown machines"), + related_name='unknown_machine_option', + ) + unknown_port = models.ForeignKey( + RadiusPolicy, + on_delete=models.PROTECT, + verbose_name=_("Policy for unknown machines"), + related_name='unknown_port_option', + ) + unknown_room = models.ForeignKey( + RadiusPolicy, + on_delete=models.PROTECT, + verbose_name=_( + "Policy for machine connecting from " + "unregistered room (relevant on ports with STRICT " + "radius mode)" + ), + related_name='unknown_room_option', + ) + non_member = models.ForeignKey( + RadiusPolicy, + on_delete=models.PROTECT, + verbose_name=_("Policy non member users."), + related_name='non_member_option', + ) + banned = models.ForeignKey( + RadiusPolicy, + on_delete=models.PROTECT, + verbose_name=_("Policy for banned users."), + related_name='banned_option' + ) + vlan_decision_ok = models.OneToOneField( + 'machines.Vlan', + on_delete=models.PROTECT, + related_name='vlan_ok_option', + blank=True, + null=True + ) + diff --git a/preferences/templates/preferences/aff_radiusoptions.html b/preferences/templates/preferences/aff_radiusoptions.html new file mode 100644 index 00000000..b434319d --- /dev/null +++ b/preferences/templates/preferences/aff_radiusoptions.html @@ -0,0 +1,96 @@ +{% comment %} +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 Hugo Levy-Falk + +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. +{% endcomment %} +{% load i18n %} +{% load acl %} +{% load logs_extra %} + + + + + + + + + + + +
{% trans "General policy for VLAN setting" %}{{ radiusoptions.radius_general_policy }}{% trans "This setting defines the VLAN policy after acceptance by RADIUS: either on the IP range's VLAN of the machine, or a VLAN preset in 'VLAN for machines accepted by RADIUS'" %}
{% trans "VLAN for machines accepted by RADIUS" %}{{ radiusoptions.vlan_decision_ok }}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
{% trans "Situation" %}{% trans "Behavior" %}
{% trans "Unknown machine" %} + {% if radiusoptions.unknown_machine.policy == 'REJECT' %} + {% trans "Reject" %} + {% else %} + Vlan {{ radiusoptions.unknown_machine.vlan }} + {% endif %} +
{% trans "Unknown port" %} + {% if radiusoptions.unknown_port.policy == 'REJECT' %} + {% trans "Reject" %} + {% else %} + Vlan {{ radiusoptions.unknown_port.vlan }} + {% endif %} +
{% trans "Unknown room" %} + {% if radiusoptions.unknown_room.policy == 'REJECT' %} + {% trans "Reject" %} + {% else %} + Vlan {{ radiusoptions.unknown_room.vlan }} + {% endif %} +
{% trans "Non member" %} + {% if radiusoptions.non_member.policy == 'REJECT' %} + {% trans "Reject" %} + {% else %} + Vlan {{ radiusoptions.non_member.vlan }} + {% endif %} +
{% trans "Banned user" %} + {% if radiusoptions.unknown_port.policy == 'REJECT' %} + {% trans "Reject" %} + {% else %} + Vlan {{ radiusoptions.banned.vlan }} + {% endif %} +
+ diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 3fde911d..21d25b43 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -32,14 +32,14 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %} - +

@@ -159,7 +159,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Edit" %} - +

@@ -208,10 +208,10 @@ with this program; if not, write to the Free Software Foundation, Inc., - - - - + + + +
{% trans "VLAN for machines rejected by RADIUS" %} {{ topologieoptions.vlan_decision_nok }}
Placement sur ce vlan par default en cas de rejet{{ topologieoptions.vlan_decision_nok }}
{% trans "VLAN for non members machines" %}{{ topologieoptions.vlan_non_member }}

Clef radius

@@ -219,7 +219,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Ajouter une clef radius {% acl_end %} {% include "preferences/aff_radiuskey.html" with radiuskey_list=radiuskey_list %} - +
@@ -236,7 +236,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Edit" %}

- + @@ -254,11 +254,11 @@ with this program; if not, write to the Free Software Foundation, Inc., - + - + @@ -287,6 +287,18 @@ with this program; if not, write to the Free Software Foundation, Inc., +
+
+

{% trans "Radius preferences" %}

+
+
+ + + {% trans "Edit" %} + + {% include "preferences/aff_radiusoptions.html" %} +
+
@@ -295,7 +307,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Web management, activé si provision automatiqueSwitchs configurés automatiquement {{ topologieoptions.provisioned_switchs|join:", " }} {% if topologieoptions.provisioned_switchs %} OK{% else %}Manquant{% endif %}
Plage d'ip de management des switchs {{ topologieoptions.switchs_ip_type }} {% if topologieoptions.switchs_ip_type %} OK{% else %}Manquant{% endif %}
Serveur des config des switchs {{ topologieoptions.switchs_management_interface }} {% if topologieoptions.switchs_management_interface %} - {{ topologieoptions.switchs_management_interface_ip }} OK{% else %}Manquant{% endif %}
- +
@@ -367,7 +378,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% can_create preferences.Reminder%} - + Ajouter un rappel

{% acl_end %} @@ -384,12 +395,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% can_create preferences.Service%} - + {% trans " Add a service" %}

{% acl_end %} {% include "preferences/aff_service.html" with service_list=service_list %} - +
@@ -400,7 +411,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
- + {% can_create preferences.MailContact %} {% trans "Add an address" %} {% acl_end %} diff --git a/preferences/urls.py b/preferences/urls.py index 63ca6a39..30163868 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -66,6 +66,11 @@ urlpatterns = [ views.edit_options, name='edit-options' ), + url( + r'^edit_options/(?P
RadiusOption)$', + views.edit_options, + name='edit-options' + ), url(r'^add_service/$', views.add_service, name='add-service'), url( r'^edit_service/(?P[0-9]+)$', diff --git a/preferences/views.py b/preferences/views.py index ee81ae89..f62a5a0d 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -62,7 +62,8 @@ from .models import ( HomeOption, Reminder, RadiusKey, - SwitchManagementCred + SwitchManagementCred, + RadiusOption, ) from . import models from . import forms @@ -86,6 +87,7 @@ def display_options(request): reminder_list = Reminder.objects.all() radiuskey_list = RadiusKey.objects.all() switchmanagementcred_list = SwitchManagementCred.objects.all() + radiusoptions, _ = RadiusOption.objects.get_or_create() return form({ 'useroptions': useroptions, 'machineoptions': machineoptions, @@ -98,7 +100,8 @@ def display_options(request): 'mailcontact_list': mailcontact_list, 'reminder_list': reminder_list, 'radiuskey_list' : radiuskey_list, - 'switchmanagementcred_list': switchmanagementcred_list, + 'switchmanagementcred_list': switchmanagementcred_list, + 'radiusoptions' : radiusoptions, }, 'preferences/display_preferences.html', request) From f0736ebc39efca3f76dcba903ad1844bdd0f7569 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Sun, 2 Dec 2018 17:03:27 +0100 Subject: [PATCH 03/12] Allow policies edition --- preferences/forms.py | 8 + .../migrations/0052_auto_20181013_1629.py | 59 ++++---- preferences/models.py | 138 ++++++++++-------- .../preferences/edit_preferences.html | 6 + preferences/views.py | 4 +- 5 files changed, 125 insertions(+), 90 deletions(-) diff --git a/preferences/forms.py b/preferences/forms.py index 2f90927f..685400de 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -42,6 +42,7 @@ from .models import ( Reminder, RadiusKey, SwitchManagementCred, + RadiusOption, ) from topologie.models import Switch @@ -229,6 +230,13 @@ class EditHomeOptionForm(ModelForm): self.fields['twitter_account_name'].label = _("Twitter account name") +class EditRadiusOptionForm(ModelForm): + """Edition forms for Radius options""" + class Meta: + model = RadiusOption + fields = ['radius_general_policy', 'vlan_decision_ok'] + + class ServiceForm(ModelForm): """Edition, ajout de services sur la page d'accueil""" class Meta: diff --git a/preferences/migrations/0052_auto_20181013_1629.py b/preferences/migrations/0052_auto_20181013_1629.py index 70498536..0f1d0b74 100644 --- a/preferences/migrations/0052_auto_20181013_1629.py +++ b/preferences/migrations/0052_auto_20181013_1629.py @@ -10,17 +10,11 @@ import re2o.mixins def create_radius_policy(apps, schema_editor): OptionalTopologie = apps.get_model('preferences', 'OptionalTopologie') RadiusOption = apps.get_model('preferences', 'RadiusOption') - RadiusPolicy = apps.get_model('preferences', 'RadiusPolicy') option,_ = OptionalTopologie.objects.get_or_create() radius_option = RadiusOption() radius_option.radius_general_policy = option.radius_general_policy - radius_option.unknown_machine = RadiusPolicy.objects.create() - radius_option.unknown_port = RadiusPolicy.objects.create() - radius_option.unknown_room = RadiusPolicy.objects.create() - radius_option.non_member = RadiusPolicy.objects.create() - radius_option.banned = RadiusPolicy.objects.create() radius_option.vlan_decision_ok = option.vlan_decision_ok radius_option.save() @@ -45,47 +39,56 @@ class Migration(migrations.Migration): }, bases=(re2o.mixins.AclMixin, models.Model), ), - migrations.CreateModel( - name='RadiusPolicy', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('policy', models.CharField(choices=[('REJECT', 'Reject the machine'), ('SET_VLAN', 'Place the machine on the VLAN')], default='REJECT', max_length=32)), - ('vlan', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.Vlan')), - ], - options={ - 'verbose_name': 'radius policy', - }, - bases=(re2o.mixins.AclMixin, models.Model), + migrations.AddField( + model_name='radiusoption', + name='banned_vlan', + field=models.ForeignKey(blank=True, help_text='Vlan for banned if not rejected.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='banned_vlan', to='machines.Vlan', verbose_name='Banned Vlan'), ), migrations.AddField( model_name='radiusoption', - name='non_member', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='non_member_option', to='preferences.RadiusPolicy', verbose_name='Policy non member users.'), + name='non_member_vlan', + field=models.ForeignKey(blank=True, help_text='Vlan for non members if not rejected.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='non_member_vlan', to='machines.Vlan', verbose_name='Non member Vlan'), ), migrations.AddField( model_name='radiusoption', - name='unknown_machine', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='unknown_machine_option', to='preferences.RadiusPolicy', verbose_name='Policy for unknown machines'), + name='unknown_machine_vlan', + field=models.ForeignKey(blank=True, help_text='Vlan for unknown machines if not rejected.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='unknown_machine_vlan', to='machines.Vlan', verbose_name='Unknown machine Vlan'), ), migrations.AddField( model_name='radiusoption', - name='unknown_port', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='unknown_port_option', to='preferences.RadiusPolicy', verbose_name='Policy for unknown machines'), + name='unknown_port_vlan', + field=models.ForeignKey(blank=True, help_text='Vlan for unknown ports if not rejected.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='unknown_port_vlan', to='machines.Vlan', verbose_name='Unknown port Vlan'), ), migrations.AddField( model_name='radiusoption', - name='unknown_room', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='unknown_room_option', to='preferences.RadiusPolicy', verbose_name='Policy for machine connecting from unregistered room (relevant on ports with STRICT radius mode)'), + name='unknown_room_vlan', + field=models.ForeignKey(blank=True, help_text='Vlan for unknown room if not rejected.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='unknown_room_vlan', to='machines.Vlan', verbose_name='Unknown room Vlan'), ), migrations.AddField( model_name='radiusoption', name='banned', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='banned_option', to='preferences.RadiusPolicy', verbose_name='Policy for banned users.'), + field=models.CharField(choices=[('REJECT', 'Reject the machine'), ('SET_VLAN', 'Place the machine on the VLAN')], default='REJECT', max_length=32, verbose_name='Policy for banned users.'), ), migrations.AddField( model_name='radiusoption', - name='vlan_decision_ok', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlan_ok_option', to='machines.Vlan'), + name='non_member', + field=models.CharField(choices=[('REJECT', 'Reject the machine'), ('SET_VLAN', 'Place the machine on the VLAN')], default='REJECT', max_length=32, verbose_name='Policy non member users.'), ), + migrations.AddField( + model_name='radiusoption', + name='unknown_machine', + field=models.CharField(choices=[('REJECT', 'Reject the machine'), ('SET_VLAN', 'Place the machine on the VLAN')], default='REJECT', max_length=32, verbose_name='Policy for unknown machines'), + ), + migrations.AddField( + model_name='radiusoption', + name='unknown_port', + field=models.CharField(choices=[('REJECT', 'Reject the machine'), ('SET_VLAN', 'Place the machine on the VLAN')], default='REJECT', max_length=32, verbose_name='Policy for unknown machines'), + ), + migrations.AddField( + model_name='radiusoption', + name='unknown_room', + field=models.CharField(choices=[('REJECT', 'Reject the machine'), ('SET_VLAN', 'Place the machine on the VLAN')], default='REJECT', max_length=32, verbose_name='Policy for machine connecting from unregistered room (relevant on ports with STRICT radius mode)'), + ), + migrations.RunPython(create_radius_policy), ] diff --git a/preferences/models.py b/preferences/models.py index f3f5527e..cbc42516 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -199,26 +199,6 @@ class OptionalTopologie(AclMixin, PreferencesModel): ('tftp', 'tftp'), ) - radius_general_policy = models.CharField( - max_length=32, - choices=CHOICE_RADIUS, - default='DEFINED' - ) - vlan_decision_ok = models.OneToOneField( - 'machines.Vlan', - on_delete=models.PROTECT, - related_name='decision_ok', - blank=True, - null=True - ) - vlan_decision_nok = models.OneToOneField( - 'machines.Vlan', - on_delete=models.PROTECT, - related_name='decision_nok', - blank=True, - null=True - ) - switchs_web_management = models.BooleanField( default=False, help_text="Web management, activé si provision automatique" @@ -589,31 +569,6 @@ class MailMessageOption(AclMixin, models.Model): verbose_name = _("email message options") -class RadiusPolicy(AclMixin, models.Model): - class Meta: - verbose_name = _('radius policy') - - REJECT = 'REJECT' - SET_VLAN = 'SET_VLAN' - CHOICE_POLICY = ( - (REJECT, _('Reject the machine')), - (SET_VLAN, _('Place the machine on the VLAN')) - ) - - policy = models.CharField( - max_length=32, - choices=CHOICE_POLICY, - default=REJECT - ) - - vlan = models.ForeignKey( - 'machines.Vlan', - on_delete=models.PROTECT, - blank=True, - null=True - ) - - class RadiusOption(AclMixin, models.Model): class Meta: verbose_name = _("radius policies") @@ -624,44 +579,105 @@ class RadiusOption(AclMixin, models.Model): (MACHINE, _("On the IP range's VLAN of the machine")), (DEFINED, _("Preset in 'VLAN for machines accepted by RADIUS'")), ) + REJECT = 'REJECT' + SET_VLAN = 'SET_VLAN' + CHOICE_POLICY = ( + (REJECT, _('Reject the machine')), + (SET_VLAN, _('Place the machine on the VLAN')) + ) radius_general_policy = models.CharField( max_length=32, choices=CHOICE_RADIUS, default='DEFINED' ) - unknown_machine = models.ForeignKey( - RadiusPolicy, - on_delete=models.PROTECT, + unknown_machine = models.CharField( + max_length=32, + choices=CHOICE_POLICY, + default=REJECT, verbose_name=_("Policy for unknown machines"), - related_name='unknown_machine_option', ) - unknown_port = models.ForeignKey( - RadiusPolicy, + unknown_machine_vlan = models.ForeignKey( + 'machines.Vlan', on_delete=models.PROTECT, + related_name='unknown_machine_vlan', + blank=True, + null=True, + verbose_name=_('Unknown machine Vlan'), + help_text=_( + 'Vlan for unknown machines if not rejected.' + ) + ) + unknown_port = models.CharField( + max_length=32, + choices=CHOICE_POLICY, + default=REJECT, verbose_name=_("Policy for unknown machines"), - related_name='unknown_port_option', ) - unknown_room = models.ForeignKey( - RadiusPolicy, + unknown_port_vlan = models.ForeignKey( + 'machines.Vlan', on_delete=models.PROTECT, + related_name='unknown_port_vlan', + blank=True, + null=True, + verbose_name=_('Unknown port Vlan'), + help_text=_( + 'Vlan for unknown ports if not rejected.' + ) + ) + unknown_room = models.CharField( + max_length=32, + choices=CHOICE_POLICY, + default=REJECT, verbose_name=_( "Policy for machine connecting from " "unregistered room (relevant on ports with STRICT " "radius mode)" ), - related_name='unknown_room_option', ) - non_member = models.ForeignKey( - RadiusPolicy, + unknown_room_vlan = models.ForeignKey( + 'machines.Vlan', + related_name='unknown_room_vlan', on_delete=models.PROTECT, + blank=True, + null=True, + verbose_name=_('Unknown room Vlan'), + help_text=_( + 'Vlan for unknown room if not rejected.' + ) + ) + non_member = models.CharField( + max_length=32, + choices=CHOICE_POLICY, + default=REJECT, verbose_name=_("Policy non member users."), - related_name='non_member_option', ) - banned = models.ForeignKey( - RadiusPolicy, + non_member_vlan = models.ForeignKey( + 'machines.Vlan', + related_name='non_member_vlan', on_delete=models.PROTECT, + blank=True, + null=True, + verbose_name=_('Non member Vlan'), + help_text=_( + 'Vlan for non members if not rejected.' + ) + ) + banned = models.CharField( + max_length=32, + choices=CHOICE_POLICY, + default=REJECT, verbose_name=_("Policy for banned users."), - related_name='banned_option' + ) + banned_vlan = models.ForeignKey( + 'machines.Vlan', + related_name='banned_vlan', + on_delete=models.PROTECT, + blank=True, + null=True, + verbose_name=_('Banned Vlan'), + help_text=_( + 'Vlan for banned if not rejected.' + ) ) vlan_decision_ok = models.OneToOneField( 'machines.Vlan', diff --git a/preferences/templates/preferences/edit_preferences.html b/preferences/templates/preferences/edit_preferences.html index a1540f33..c3dd4652 100644 --- a/preferences/templates/preferences/edit_preferences.html +++ b/preferences/templates/preferences/edit_preferences.html @@ -37,6 +37,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %} {% massive_bootstrap_form options 'utilisateur_asso,automatic_provision_switchs' %} + {% if formset %} + {{ formset.management_form }} + {% for f in formset %} + {% bootstrap_form f %} + {% endfor %} + {% endif %} {% trans "Edit" as tr_edit %} {% bootstrap_button tr_edit button_type="submit" icon='ok' button_class='btn-success' %}
diff --git a/preferences/views.py b/preferences/views.py index f62a5a0d..586be60f 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -137,7 +137,9 @@ def edit_options(request, section): messages.success(request, _("The preferences were edited.")) return redirect(reverse('preferences:display-options')) return form( - {'options': options}, + { + 'options': options, + }, 'preferences/edit_preferences.html', request ) From 9506bd400244dfa91067ec2c66167dcba7b2d10d Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Tue, 4 Dec 2018 14:58:28 +0100 Subject: [PATCH 04/12] Fixing migrations is more a way of life than a way to have fun. --- ..._20181013_1629.py => 0056_radiusoption.py} | 19 +++++++++++++++++- .../migrations/0057_auto_20181204_0757.py | 20 +++++++++++++++++++ preferences/models.py | 2 +- 3 files changed, 39 insertions(+), 2 deletions(-) rename preferences/migrations/{0052_auto_20181013_1629.py => 0056_radiusoption.py} (87%) create mode 100644 preferences/migrations/0057_auto_20181204_0757.py diff --git a/preferences/migrations/0052_auto_20181013_1629.py b/preferences/migrations/0056_radiusoption.py similarity index 87% rename from preferences/migrations/0052_auto_20181013_1629.py rename to preferences/migrations/0056_radiusoption.py index 0f1d0b74..e329f598 100644 --- a/preferences/migrations/0052_auto_20181013_1629.py +++ b/preferences/migrations/0056_radiusoption.py @@ -24,7 +24,7 @@ class Migration(migrations.Migration): dependencies = [ ('machines', '0095_auto_20180919_2225'), - ('preferences', '0051_auto_20180919_2225'), + ('preferences', '0055_generaloption_main_site_url'), ] operations = [ @@ -89,6 +89,23 @@ class Migration(migrations.Migration): name='unknown_room', field=models.CharField(choices=[('REJECT', 'Reject the machine'), ('SET_VLAN', 'Place the machine on the VLAN')], default='REJECT', max_length=32, verbose_name='Policy for machine connecting from unregistered room (relevant on ports with STRICT radius mode)'), ), + migrations.AddField( + model_name='radiusoption', + name='vlan_decision_ok', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlan_ok_option', to='machines.Vlan'), + ), migrations.RunPython(create_radius_policy), + migrations.RemoveField( + model_name='optionaltopologie', + name='radius_general_policy', + ), + migrations.RemoveField( + model_name='optionaltopologie', + name='vlan_decision_nok', + ), + migrations.RemoveField( + model_name='optionaltopologie', + name='vlan_decision_ok', + ), ] diff --git a/preferences/migrations/0057_auto_20181204_0757.py b/preferences/migrations/0057_auto_20181204_0757.py new file mode 100644 index 00000000..ba4e1a6f --- /dev/null +++ b/preferences/migrations/0057_auto_20181204_0757.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-12-04 13:57 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0056_radiusoption'), + ] + + operations = [ + migrations.AlterField( + model_name='radiusoption', + name='unknown_port', + field=models.CharField(choices=[('REJECT', 'Reject the machine'), ('SET_VLAN', 'Place the machine on the VLAN')], default='REJECT', max_length=32, verbose_name='Policy for unknown port'), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index cbc42516..4e6ebd6b 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -611,7 +611,7 @@ class RadiusOption(AclMixin, models.Model): max_length=32, choices=CHOICE_POLICY, default=REJECT, - verbose_name=_("Policy for unknown machines"), + verbose_name=_("Policy for unknown port"), ) unknown_port_vlan = models.ForeignKey( 'machines.Vlan', From b269a5f6ef5cb7e63c2c2c8b892c06839bb0b2b5 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Tue, 4 Dec 2018 14:59:23 +0100 Subject: [PATCH 05/12] Allow policies editions through form --- preferences/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preferences/forms.py b/preferences/forms.py index 685400de..1126b399 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -234,7 +234,7 @@ class EditRadiusOptionForm(ModelForm): """Edition forms for Radius options""" class Meta: model = RadiusOption - fields = ['radius_general_policy', 'vlan_decision_ok'] + fields = '__all__' class ServiceForm(ModelForm): From 6df47b08ec366b55aab10e55780956725a94b821 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Tue, 4 Dec 2018 18:46:57 +0100 Subject: [PATCH 06/12] Adapt preferences' display to new model layout --- .../preferences/aff_radiusoptions.html | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/preferences/templates/preferences/aff_radiusoptions.html b/preferences/templates/preferences/aff_radiusoptions.html index b434319d..60ee15f5 100644 --- a/preferences/templates/preferences/aff_radiusoptions.html +++ b/preferences/templates/preferences/aff_radiusoptions.html @@ -45,50 +45,50 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Unknown machine" %} - {% if radiusoptions.unknown_machine.policy == 'REJECT' %} + {% if radiusoptions.unknown_machine == 'REJECT' %} {% trans "Reject" %} {% else %} - Vlan {{ radiusoptions.unknown_machine.vlan }} + Vlan {{ radiusoptions.unknown_machine_vlan }} {% endif %} {% trans "Unknown port" %} - {% if radiusoptions.unknown_port.policy == 'REJECT' %} + {% if radiusoptions.unknown_port == 'REJECT' %} {% trans "Reject" %} {% else %} - Vlan {{ radiusoptions.unknown_port.vlan }} + Vlan {{ radiusoptions.unknown_port_vlan }} {% endif %} {% trans "Unknown room" %} - {% if radiusoptions.unknown_room.policy == 'REJECT' %} + {% if radiusoptions.unknown_room == 'REJECT' %} {% trans "Reject" %} {% else %} - Vlan {{ radiusoptions.unknown_room.vlan }} + Vlan {{ radiusoptions.unknown_room_vlan }} {% endif %} {% trans "Non member" %} - {% if radiusoptions.non_member.policy == 'REJECT' %} + {% if radiusoptions.non_member == 'REJECT' %} {% trans "Reject" %} {% else %} - Vlan {{ radiusoptions.non_member.vlan }} + Vlan {{ radiusoptions.non_member_vlan }} {% endif %} {% trans "Banned user" %} - {% if radiusoptions.unknown_port.policy == 'REJECT' %} + {% if radiusoptions.unknown_port == 'REJECT' %} {% trans "Reject" %} {% else %} - Vlan {{ radiusoptions.banned.vlan }} + Vlan {{ radiusoptions.banned_vlan }} {% endif %} From 2702d7c11635162c4773e75a7de0e44058b12ab7 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Tue, 4 Dec 2018 20:00:18 +0100 Subject: [PATCH 07/12] Radius opers according to policies. --- freeradius_utils/auth.py | 205 +++++++++++++++++++++++++++------------ 1 file changed, 145 insertions(+), 60 deletions(-) diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index 9bd56015..922fd153 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -57,14 +57,12 @@ application = get_wsgi_application() from machines.models import Interface, IpList, Nas, Domain from topologie.models import Port, Switch from users.models import User -from preferences.models import OptionalTopologie +from preferences.models import RadiusOption -options, created = OptionalTopologie.objects.get_or_create() -VLAN_NOK = options.vlan_decision_nok.vlan_id -VLAN_OK = options.vlan_decision_ok.vlan_id -VLAN_NON_MEMBER = options.vlan_non_member.vlan_id -RADIUS_POLICY = options.radius_general_policy +OPTIONS, created = RadiusOption.objects.get_or_create() +VLAN_OK = OPTIONS.vlan_decision_ok.vlan_id +RADIUS_POLICY = OPTIONS.radius_general_policy #: Serveur radius de test (pas la prod) TEST_SERVER = bool(os.getenv('DBG_FREERADIUS', False)) @@ -288,6 +286,7 @@ def find_nas_from_request(nas_id): .select_related('machine__switch__stack')) return nas.first() + def check_user_machine_and_register(nas_type, username, mac_address): """Verifie le username et la mac renseignee. L'enregistre si elle est inconnue. @@ -328,27 +327,44 @@ def check_user_machine_and_register(nas_type, username, mac_address): def decide_vlan_switch(nas_machine, nas_type, port_number, - mac_address): + mac_address): """Fonction de placement vlan pour un switch en radius filaire auth par mac. Plusieurs modes : - - nas inconnu, port inconnu : on place sur le vlan par defaut VLAN_OK - - pas de radius sur le port : VLAN_OK - - bloq : VLAN_NOK - - force : placement sur le vlan indiqué dans la bdd - - mode strict : - - pas de chambre associée : VLAN_NOK - - pas d'utilisateur dans la chambre : VLAN_NOK - - cotisation non à jour : VLAN_NON_MEMBER - - sinon passe à common (ci-dessous) - - mode common : - - interface connue (macaddress): - - utilisateur proprio non cotisant : VLAN_NON_MEMBER - - utilisateur proprio banni : VLAN_NOK - - user à jour : VLAN_OK - - interface inconnue : - - register mac désactivé : VLAN_NON_MEMBER - - register mac activé -> redirection vers webauth + - tous les modes: + - nas inconnu: VLAN_OK + - port inconnu: Politique définie dans RadiusOption + - pas de radius sur le port: VLAN_OK + - force: placement sur le vlan indiqué dans la bdd + - mode strict: + - pas de chambre associée: Politique définie + dans RadiusOption + - pas d'utilisateur dans la chambre : Rejet + (redirection web si disponible) + - utilisateur de la chambre banni ou désactivé : Rejet + (redirection web si disponible) + - utilisateur de la chambre non cotisant et non whiteslist: + Politique définie dans RadiusOption + + - sinon passe à common (ci-dessous) + - mode common : + - interface connue (macaddress): + - utilisateur proprio non cotisant / machine désactivée: + Politique définie dans RadiusOption + - utilisateur proprio banni : + Politique définie dans RadiusOption + - user à jour : VLAN_OK (réassignation de l'ipv4 au besoin) + - interface inconnue : + - register mac désactivé : Politique définie + dans RadiusOption + - register mac activé: redirection vers webauth + Returns: + tuple avec : + - Nom du switch (str) + - chambre (str) + - raison de la décision (str) + - vlan_id (int) + - decision (bool) """ # Get port from switch and port number extra_log = "" @@ -369,7 +385,13 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, # Aucune information particulière ne permet de déterminer quelle # politique à appliquer sur ce port if not port: - return (sw_name, "Chambre inconnue", u'Port inconnu', VLAN_OK, True) + return ( + sw_name, + "Chambre inconnue", + u'Port inconnu', + OPTIONS.unknown_port_vlan.vlan_id, + OPTIONS.unknown_port != OPTIONS.REJECT + ) # On récupère le profil du port port_profile = port.get_port_profile @@ -382,9 +404,9 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, else: DECISION_VLAN = VLAN_OK - # Si le port est désactivé, on rejette sur le vlan de déconnexion + # Si le port est désactivé, on rejette la connexion if not port.state: - return (sw_name, port.room, u'Port desactivé', VLAN_NOK, True) + return (sw_name, port.room, u'Port desactivé', None, False) # Si radius est désactivé, on laisse passer if port_profile.radius_type == 'NO': @@ -394,35 +416,68 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, DECISION_VLAN, True) - # Si le 802.1X est activé sur ce port, cela veut dire que la personne a été accept précédemment + # Si le 802.1X est activé sur ce port, cela veut dire que la personne a + # été accept précédemment # Par conséquent, on laisse passer sur le bon vlan - if nas_type.port_access_mode == '802.1X' and port_profile.radius_type == '802.1X': + if (nas_type.port_access_mode, port_profile.radius_type) == ('802.1X', '802.1X'): room = port.room or "Chambre/local inconnu" - return (sw_name, room, u'Acceptation authentification 802.1X', DECISION_VLAN, True) + return ( + sw_name, + room, + u'Acceptation authentification 802.1X', + DECISION_VLAN, + True + ) # Sinon, cela veut dire qu'on fait de l'auth radius par mac # Si le port est en mode strict, on vérifie que tous les users - # rattachés à ce port sont bien à jour de cotisation. Sinon on rejette (anti squattage) - # Il n'est pas possible de se connecter sur une prise strict sans adhérent à jour de cotis - # dedans + # rattachés à ce port sont bien à jour de cotisation. Sinon on rejette + # (anti squattage) + # Il n'est pas possible de se connecter sur une prise strict sans adhérent + # à jour de cotis dedans if port_profile.radius_mode == 'STRICT': room = port.room if not room: - return (sw_name, "Inconnue", u'Chambre inconnue', VLAN_NOK, True) + return ( + sw_name, + "Inconnue", + u'Chambre inconnue', + OPTIONS.unknown_room_vlan.vlan_id, + OPTIONS.unknown_room != OPTIONS.REJECT + ) room_user = User.objects.filter( Q(club__room=port.room) | Q(adherent__room=port.room) ) if not room_user: - return (sw_name, room, u'Chambre non cotisante -> Web redirect', None, False) + return ( + sw_name, + room, + u'Chambre non cotisante -> Web redirect', + None, + False + ) for user in room_user: if user.is_ban() or user.state != User.STATE_ACTIVE: - return (sw_name, room, u'Chambre resident desactive -> Web redirect', None, False) + return ( + sw_name, + room, + u'Utilisateur banni ou désactivé -> Web redirect', + None, + False + ) elif not (user.is_connected() or user.is_whitelisted()): - return (sw_name, room, u'Utilisateur non cotisant', VLAN_NON_MEMBER) + return ( + sw_name, + room, + u'Utilisateur non cotisant', + OPTIONS.non_member_vlan.vlan_id, + OPTIONS.non_member != OPTIONS.REJECT + ) # else: user OK, on passe à la verif MAC - # Si on fait de l'auth par mac, on cherche l'interface via sa mac dans la bdd + # Si on fait de l'auth par mac, on cherche l'interface + # via sa mac dans la bdd if port_profile.radius_mode == 'COMMON' or port_profile.radius_mode == 'STRICT': # Authentification par mac interface = (Interface.objects @@ -432,37 +487,67 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, .first()) if not interface: room = port.room - # On essaye de register la mac, si l'autocapture a été activée - # Sinon on rejette sur vlan_nok - if not nas_type.autocapture_mac: - return (sw_name, "", u'Machine inconnue', VLAN_NON_MEMBER) - # On rejette pour basculer sur du webauth + # On essaye de register la mac, si l'autocapture a été activée, + # on rejette pour faire une redirection web si possible. + if nas_type.autocapture_mac: + return ( + sw_name, + room, + u'Machine Inconnue -> Web redirect', + None, + False + ) + # Sinon on bascule sur la politique définie dans les options + # radius. else: - return (sw_name, room, u'Machine Inconnue -> Web redirect', None, False) + return ( + sw_name, + "", + u'Machine inconnue', + OPTIONS.unknown_machine_vlan.vlan_id, + OPTIONS.unknown_machine != OPTIONS.REJECT + ) - # L'interface a été trouvée, on vérifie qu'elle est active, sinon on reject + # L'interface a été trouvée, on vérifie qu'elle est active, + # sinon on reject # Si elle n'a pas d'ipv4, on lui en met une # Enfin on laisse passer sur le vlan pertinent else: room = port.room + if interface.machine.user.is_banned(): + return ( + sw_name, + room, + u'Adherent banni', + OPTIONS.banned_vlan.vlan_id, + OPTIONS.banned != OPTIONS.REJECT + ) if not interface.is_active: - return (sw_name, - room, - u'Machine non active / adherent non cotisant', - VLAN_NON_MEMBER) - ## Si on choisi de placer les machines sur le vlan correspondant à leur type : + return ( + sw_name, + room, + u'Machine non active / adherent non cotisant', + OPTIONS.non_member_vlan.vlan_id, + OPTIONS.non_member != OPTIONS.REJECT + ) + # Si on choisi de placer les machines sur le vlan + # correspondant à leur type : if RADIUS_POLICY == 'MACHINE': DECISION_VLAN = interface.type.ip_type.vlan.vlan_id if not interface.ipv4: interface.assign_ipv4() - return (sw_name, - room, - u"Ok, Reassignation de l'ipv4" + extra_log, - DECISION_VLAN, - True) + return ( + sw_name, + room, + u"Ok, Reassignation de l'ipv4" + extra_log, + DECISION_VLAN, + True + ) else: - return (sw_name, - room, - u'Machine OK' + extra_log, - DECISION_VLAN, - True) + return ( + sw_name, + room, + u'Machine OK' + extra_log, + DECISION_VLAN, + True + ) From b2be55a909b0b2e96cc63c5db08298c23d7b0744 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Wed, 5 Dec 2018 12:19:29 +0100 Subject: [PATCH 08/12] =?UTF-8?q?Typo=20sur=20la=20v=C3=A9rification=20de?= =?UTF-8?q?=20bannissement.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- freeradius_utils/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index 922fd153..66465c0e 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -514,7 +514,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, # Enfin on laisse passer sur le vlan pertinent else: room = port.room - if interface.machine.user.is_banned(): + if interface.machine.user.is_ban(): return ( sw_name, room, From 0e2f1258d429c1e33f6327413d00e9803de853b0 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Wed, 5 Dec 2018 12:28:07 +0100 Subject: [PATCH 09/12] Add PreferencesModel as ancestor of RadiusOption --- preferences/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preferences/models.py b/preferences/models.py index 4e6ebd6b..4644aa1c 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -569,7 +569,7 @@ class MailMessageOption(AclMixin, models.Model): verbose_name = _("email message options") -class RadiusOption(AclMixin, models.Model): +class RadiusOption(AclMixin, PreferencesModel): class Meta: verbose_name = _("radius policies") From 7e9de612fa48fd879a5a8322276fb6f9ed4f2c6f Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Wed, 5 Dec 2018 23:14:53 +0100 Subject: [PATCH 10/12] Prettier display of vlan OK --- preferences/templates/preferences/aff_radiusoptions.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preferences/templates/preferences/aff_radiusoptions.html b/preferences/templates/preferences/aff_radiusoptions.html index 60ee15f5..17e2a869 100644 --- a/preferences/templates/preferences/aff_radiusoptions.html +++ b/preferences/templates/preferences/aff_radiusoptions.html @@ -31,7 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "VLAN for machines accepted by RADIUS" %} - {{ radiusoptions.vlan_decision_ok }} + Vlan {{ radiusoptions.vlan_decision_ok }}
From 789a648bd0cf1ac87a8fec71b951ee0406ef3b6f Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Sat, 8 Dec 2018 21:47:21 +0100 Subject: [PATCH 11/12] cached values for radius policies --- freeradius_utils/auth.py | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index 66465c0e..3fa7e23f 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -60,10 +60,6 @@ from users.models import User from preferences.models import RadiusOption -OPTIONS, created = RadiusOption.objects.get_or_create() -VLAN_OK = OPTIONS.vlan_decision_ok.vlan_id -RADIUS_POLICY = OPTIONS.radius_general_policy - #: Serveur radius de test (pas la prod) TEST_SERVER = bool(os.getenv('DBG_FREERADIUS', False)) @@ -370,7 +366,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, extra_log = "" # Si le NAS est inconnu, on place sur le vlan defaut if not nas_machine: - return ('?', u'Chambre inconnue', u'Nas inconnu', VLAN_OK, True) + return ('?', u'Chambre inconnue', u'Nas inconnu', RadiusOption.get_cached_value('vlan_decision_ok').vlan_id, True) sw_name = str(getattr(nas_machine, 'short_name', str(nas_machine))) @@ -389,8 +385,8 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, sw_name, "Chambre inconnue", u'Port inconnu', - OPTIONS.unknown_port_vlan.vlan_id, - OPTIONS.unknown_port != OPTIONS.REJECT + RadiusOption('unknown_port_vlan').vlan_id, + RadiusOption('unknown_port')!= RadiusOption.REJECT ) # On récupère le profil du port @@ -402,7 +398,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, DECISION_VLAN = int(port_profile.vlan_untagged.vlan_id) extra_log = u"Force sur vlan " + str(DECISION_VLAN) else: - DECISION_VLAN = VLAN_OK + DECISION_VLAN = RadiusOption.get_cached_value('vlan_decision_ok') # Si le port est désactivé, on rejette la connexion if not port.state: @@ -442,8 +438,8 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, sw_name, "Inconnue", u'Chambre inconnue', - OPTIONS.unknown_room_vlan.vlan_id, - OPTIONS.unknown_room != OPTIONS.REJECT + RadiusOption('unknown_room_vlan').vlan_id, + RadiusOption('unknown_room')!= RadiusOption.REJECT ) room_user = User.objects.filter( @@ -471,8 +467,8 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, sw_name, room, u'Utilisateur non cotisant', - OPTIONS.non_member_vlan.vlan_id, - OPTIONS.non_member != OPTIONS.REJECT + RadiusOption('non_member_vlan').vlan_id, + RadiusOption('non_member')!= RadiusOption.REJECT ) # else: user OK, on passe à la verif MAC @@ -504,8 +500,8 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, sw_name, "", u'Machine inconnue', - OPTIONS.unknown_machine_vlan.vlan_id, - OPTIONS.unknown_machine != OPTIONS.REJECT + RadiusOption('unknown_machine_vlan').vlan_id, + RadiusOption('unknown_machine')!= RadiusOption.REJECT ) # L'interface a été trouvée, on vérifie qu'elle est active, @@ -519,20 +515,20 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, sw_name, room, u'Adherent banni', - OPTIONS.banned_vlan.vlan_id, - OPTIONS.banned != OPTIONS.REJECT + RadiusOption('banned_vlan').vlan_id, + RadiusOption('banned')!= RadiusOption.REJECT ) if not interface.is_active: return ( sw_name, room, u'Machine non active / adherent non cotisant', - OPTIONS.non_member_vlan.vlan_id, - OPTIONS.non_member != OPTIONS.REJECT + RadiusOption('non_member_vlan').vlan_id, + RadiusOption('non_member')!= RadiusOption.REJECT ) # Si on choisi de placer les machines sur le vlan # correspondant à leur type : - if RADIUS_POLICY == 'MACHINE': + if RadiusOption.get_cached_value('radius_general_policy') == 'MACHINE': DECISION_VLAN = interface.type.ip_type.vlan.vlan_id if not interface.ipv4: interface.assign_ipv4() From a15d50bebe1b2e4710af81d45925bd53d2bd5a3a Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Sat, 8 Dec 2018 23:07:49 +0100 Subject: [PATCH 12/12] RadiusOption Display --- .../preferences/display_preferences.html | 614 +++++++++--------- 1 file changed, 307 insertions(+), 307 deletions(-) diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 21d25b43..0e34d6e9 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -33,203 +33,203 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %}
-
- -
+
- - {% trans "Edit" %} + + {% trans "Edit" %} -

+

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{% trans "Website name" %}{{ generaloptions.site_name }}{% trans "Email address for automatic emailing" %}{{ generaloptions.email_from }}
{% trans "Number of results displayed when searching" %}{{ generaloptions.search_display_page }}{% trans "Number of items per page (standard size)" %}{{ generaloptions.pagination_number }}
{% trans "Number of items per page (large size)" %}{{ generaloptions.pagination_large_number }}{% trans "Time before expiration of the reset password link (in hours)" %}{{ generaloptions.req_expire_hrs }}
{% trans "General message displayed on the website" %}{{ generaloptions.general_message }}{% trans "Main site url" %}{{ generaloptions.main_site_url }}
{% trans "Summary of the General Terms of Use" %}{{ generaloptions.GTU_sum_up }}{% trans "General Terms of Use" %}{{ generaloptions.GTU }} -
- - - - - - - - - - - -
{% trans "Local email accounts enabled" %}{{ useroptions.local_email_accounts_enabled|tick }}{% trans "Local email domain" %}{{ useroptions.local_email_domain }}
{% trans "Maximum number of email aliases allowed" %}{{ useroptions.max_email_address }}
-
-
- -
-
-

- {% trans "User preferences" %} -

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{% trans "Website name" %}{{ generaloptions.site_name }}{% trans "Email address for automatic emailing" %}{{ generaloptions.email_from }}
{% trans "Number of results displayed when searching" %}{{ generaloptions.search_display_page }}{% trans "Number of items per page (standard size)" %}{{ generaloptions.pagination_number }}
{% trans "Number of items per page (large size)" %}{{ generaloptions.pagination_large_number }}{% trans "Time before expiration of the reset password link (in hours)" %}{{ generaloptions.req_expire_hrs }}
{% trans "General message displayed on the website" %}{{ generaloptions.general_message }}{% trans "Main site url" %}{{ generaloptions.main_site_url }}
{% trans "Summary of the General Terms of Use" %}{{ generaloptions.GTU_sum_up }}{% trans "General Terms of Use" %}{{ generaloptions.GTU }} +
+ + + + + + + + + + + +
{% trans "Local email accounts enabled" %}{{ useroptions.local_email_accounts_enabled|tick }}{% trans "Local email domain" %}{{ useroptions.local_email_domain }}
{% trans "Maximum number of email aliases allowed" %}{{ useroptions.max_email_address }}
+
-
-

- - - {% trans "Edit" %} - -

+
+ +
- - - - - - - - - - - - - +

+ + + {% trans "Edit" %} + +

+ +
{% trans "Creation of members by everyone" %}{{ useroptions.all_can_create_adherent|tick }}{% trans "Creation of clubs by everyone" %}{{ useroptions.all_can_create_club|tick }}
{% trans "Self registration" %}{{ useroptions.self_adhesion|tick }}{% trans "Delete not yet active users after" %}{{ useroptions.delete_notyetactive }} days
+ + + + + + + + + + + +
{% trans "Creation of members by everyone" %}{{ useroptions.all_can_create_adherent|tick }}{% trans "Creation of clubs by everyone" %}{{ useroptions.all_can_create_club|tick }}
{% trans "Self registration" %}{{ useroptions.self_adhesion|tick }}{% trans "Delete not yet active users after" %}{{ useroptions.delete_notyetactive }} days

{% trans "Users general permissions" %}

- - - - - - - - - - - - - - - - -
{% trans "Default shell for users" %}{{ useroptions.shell_default }}{% trans "Users can edit their shell" %}{{ useroptions.self_change_shell|tick }}
{% trans "Users can edit their room" %}{{ useroptions.self_change_room|tick }}{% trans "Telephone number required" %}{{ useroptions.is_tel_mandatory|tick }}
{% trans "GPG fingerprint field" %}{{ useroptions.gpg_fingerprint|tick }}
-
+ + {% trans "Default shell for users" %} + {{ useroptions.shell_default }} + {% trans "Users can edit their shell" %} + {{ useroptions.self_change_shell|tick }} + + + {% trans "Users can edit their room" %} + {{ useroptions.self_change_room|tick }} + {% trans "Telephone number required" %} + {{ useroptions.is_tel_mandatory|tick }} + + + {% trans "GPG fingerprint field" %} + {{ useroptions.gpg_fingerprint|tick }} + +
+
-
- +
+ -
+
- - - {% trans "Edit" %} - -

- - - - - - - - - - - - - - - - - -
{% trans "Password per machine" %}{{ machineoptions.password_machine|tick }}{% trans "Maximum number of interfaces allowed for a standard user" %}{{ machineoptions.max_lambdauser_interfaces }}
{% trans "Maximum number of DNS aliases allowed for a standard user" %}{{ machineoptions.max_lambdauser_aliases }}{% trans "IPv6 support" %}{{ machineoptions.ipv6_mode }}
{% trans "Creation of machines" %}{{ machineoptions.create_machine|tick }}
-
-
+ + + {% trans "Edit" %} + +

+ + + + + + + + + + + + + + + + + +
{% trans "Password per machine" %}{{ machineoptions.password_machine|tick }}{% trans "Maximum number of interfaces allowed for a standard user" %}{{ machineoptions.max_lambdauser_interfaces }}
{% trans "Maximum number of DNS aliases allowed for a standard user" %}{{ machineoptions.max_lambdauser_aliases }}{% trans "IPv6 support" %}{{ machineoptions.ipv6_mode }}
{% trans "Creation of machines" %}{{ machineoptions.create_machine|tick }}
+
+
-
- -
+
+ +
{% trans "Edit" %}

- - - - - - - - - - - - - - - - - -
{% trans "General policy for VLAN setting" %}{{ topologieoptions.radius_general_policy }}{% trans "This setting defines the VLAN policy after acceptance by RADIUS: either on the IP range's VLAN of the machine, or a VLAN preset in 'VLAN for machines accepted by RADIUS'" %}
{% trans "VLAN for machines accepted by RADIUS" %}{{ topologieoptions.vlan_decision_ok }}{% trans "VLAN for machines rejected by RADIUS" %}{{ topologieoptions.vlan_decision_nok }}
{% trans "VLAN for non members machines" %}{{ topologieoptions.vlan_non_member }}
+ + + + + + + + + + + + + + + + + +
{% trans "General policy for VLAN setting" %}{{ topologieoptions.radius_general_policy }}{% trans "This setting defines the VLAN policy after acceptance by RADIUS: either on the IP range's VLAN of the machine, or a VLAN preset in 'VLAN for machines accepted by RADIUS'" %}
{% trans "VLAN for machines accepted by RADIUS" %}{{ topologieoptions.vlan_decision_ok }}{% trans "VLAN for machines rejected by RADIUS" %}{{ topologieoptions.vlan_decision_nok }}
{% trans "VLAN for non members machines" %}{{ topologieoptions.vlan_non_member }}
-

Clef radius

- {% can_create RadiusKey%} - Ajouter une clef radius - {% acl_end %} - {% include "preferences/aff_radiuskey.html" with radiuskey_list=radiuskey_list %} +

Clef radius

+ {% can_create RadiusKey%} + Ajouter une clef radius + {% acl_end %} + {% include "preferences/aff_radiuskey.html" with radiuskey_list=radiuskey_list %} -
-
+
+
-
+
- +
@@ -238,72 +238,72 @@ with this program; if not, write to the Free Software Foundation, Inc.,

- - - - - - -
Web management, activé si provision automatique{{ topologieoptions.switchs_web_management }}Rest management, activé si provision auto{{ topologieoptions.switchs_rest_management }}
+ + Web management, activé si provision automatique + {{ topologieoptions.switchs_web_management }} + Rest management, activé si provision auto + {{ topologieoptions.switchs_rest_management }} + + -
{% if topologieoptions.provision_switchs_enabled %}Provision de la config des switchs{% else %}Provision de la config des switchs{% endif%}
- - - - - - - - - - - - - - - - - - - - - - - - - -
Switchs configurés automatiquement{{ topologieoptions.provisioned_switchs|join:", " }} {% if topologieoptions.provisioned_switchs %} OK{% else %}Manquant{% endif %}
Plage d'ip de management des switchs{{ topologieoptions.switchs_ip_type }} {% if topologieoptions.switchs_ip_type %} OK{% else %}Manquant{% endif %}
Serveur des config des switchs{{ topologieoptions.switchs_management_interface }} {% if topologieoptions.switchs_management_interface %} - {{ topologieoptions.switchs_management_interface_ip }} OK{% else %}Manquant{% endif %}
Mode de provision des switchs{{ topologieoptions.switchs_provision }}
Mode TFTP OK
Mode SFTP{% if topologieoptions.switchs_management_sftp_creds %} OK{% else %}Creds manquants{% endif %}
+
{% if topologieoptions.provision_switchs_enabled %}Provision de la config des switchs{% else %}Provision de la config des switchs{% endif%}
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Switchs configurés automatiquement{{ topologieoptions.provisioned_switchs|join:", " }} {% if topologieoptions.provisioned_switchs %} OK{% else %}Manquant{% endif %}
Plage d'ip de management des switchs{{ topologieoptions.switchs_ip_type }} {% if topologieoptions.switchs_ip_type %} OK{% else %}Manquant{% endif %}
Serveur des config des switchs{{ topologieoptions.switchs_management_interface }} {% if topologieoptions.switchs_management_interface %} - {{ topologieoptions.switchs_management_interface_ip }} OK{% else %}Manquant{% endif %}
Mode de provision des switchs{{ topologieoptions.switchs_provision }}
Mode TFTP OK
Mode SFTP{% if topologieoptions.switchs_management_sftp_creds %} OK{% else %}Creds manquants{% endif %}
-
Creds de management des switchs
- {% can_create SwitchManagementCred%} -
Ajouter un id/mdp de management switch - {% acl_end %} -

-

- {% if switchmanagementcred_list %} OK{% else %}Manquant{% endif %} - {% include "preferences/aff_switchmanagementcred.html" with switchmanagementcred_list=switchmanagementcred_list %} -
-
+
Creds de management des switchs
+ {% can_create SwitchManagementCred%} + Ajouter un id/mdp de management switch + {% acl_end %} +

+

+ {% if switchmanagementcred_list %} OK{% else %}Manquant{% endif %} + {% include "preferences/aff_switchmanagementcred.html" with switchmanagementcred_list=switchmanagementcred_list %} +
+
-
-
-

{% trans "Radius preferences" %}

-
-
- - - {% trans "Edit" %} - - {% include "preferences/aff_radiusoptions.html" %} -
-
+
+ +
+ + + {% trans "Edit" %} + + {% include "preferences/aff_radiusoptions.html" %} +
+
-
+
@@ -312,43 +312,43 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Edit" %}

- - - - - - - - - - - - - - - - - - - - - - - - - -
{% trans "Name" %}{{ assooptions.name }}{% trans "SIRET number" %}{{ assooptions.siret }}
{% trans "Address" %}{{ assooptions.adresse1 }}
- {{ assooptions.adresse2 }} -
{% trans "Contact email address" %}{{ assooptions.contact }}
{% trans "Telephone number" %}{{ assooptions.telephone }}{% trans "Usual name" %}{{ assooptions.pseudo }}
{% trans "User object of the organisation" %}{{ assooptions.utilisateur_asso }}{% trans "Description of the organisation" %}{{ assooptions.description|safe }}
-
+ + + + + + + + + + + + + + + + + + + + + + + + + +
{% trans "Name" %}{{ assooptions.name }}{% trans "SIRET number" %}{{ assooptions.siret }}
{% trans "Address" %}{{ assooptions.adresse1 }}
+ {{ assooptions.adresse2 }} +
{% trans "Contact email address" %}{{ assooptions.contact }}
{% trans "Telephone number" %}{{ assooptions.telephone }}{% trans "Usual name" %}{{ assooptions.pseudo }}
{% trans "User object of the organisation" %}{{ assooptions.utilisateur_asso }}{% trans "Description of the organisation" %}{{ assooptions.description|safe }}
+
-
+
@@ -357,26 +357,26 @@ with this program; if not, write to the Free Software Foundation, Inc.,

- - - - - - - - - -
{% trans "Welcome email (in French)" %}{{ mailmessageoptions.welcome_mail_fr|safe }}
{% trans "Welcome email (in English)" %}{{ mailmessageoptions.welcome_mail_en|safe }}
-
-
+ + + + + + + + + +
{% trans "Welcome email (in French)" %}{{ mailmessageoptions.welcome_mail_fr|safe }}
{% trans "Welcome email (in English)" %}{{ mailmessageoptions.welcome_mail_en|safe }}
+
+ -
+
- +
{% can_create preferences.Reminder%} Ajouter un rappel @@ -384,13 +384,13 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} {% include "preferences/aff_reminder.html" with reminder_list=reminder_list %}
-
+
-
+
@@ -401,16 +401,16 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} {% include "preferences/aff_service.html" with service_list=service_list %} -
-
+
+ -
+
- +
{% can_create preferences.MailContact %} {% trans "Add an address" %} @@ -419,12 +419,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,

{% include "preferences/aff_mailcontact.html" with mailcontact_list=mailcontact_list %}
-
+
-
+
@@ -434,20 +434,20 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Edit" %}

- - - - - - - - - - - -
{% trans "Twitter account URL" %}{{ homeoptions.twitter_url }}{% trans "Twitter account name" %}{{ homeoptions.twitter_account_name }}
{% trans "Facebook account URL" %}{{ homeoptions.facebook_url }}
-
+ + + + + + + + + + + +
{% trans "Twitter account URL" %}{{ homeoptions.twitter_url }}{% trans "Twitter account name" %}{{ homeoptions.twitter_account_name }}
{% trans "Facebook account URL" %}{{ homeoptions.facebook_url }}
+
{% endblock %}