models.py 22 KB
Newer Older
1
# -*- mode: python; coding: utf-8 -*-
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# Re2o 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.
23 24 25
"""
Reglages généraux, machines, utilisateurs, mail, general pour l'application.
"""
26
from __future__ import unicode_literals
27

28
from django.utils.functional import cached_property
29
from django.utils import timezone
30
from django.db import models
31
from django.db.models.signals import post_save
32
from django.dispatch import receiver
33
from django.core.cache import cache
34
from django.forms import ValidationError
35
from django.utils.translation import ugettext_lazy as _
36

37
import machines.models
38

chirac's avatar
chirac committed
39
from re2o.mixins import AclMixin
40
from re2o.aes_field import AESEncryptedField
41

42
from datetime import timedelta
43

Maël Kervella's avatar
Maël Kervella committed
44

Gabriel Detraz's avatar
Gabriel Detraz committed
45
class PreferencesModel(models.Model):
46 47 48
    """ Base object for the Preferences objects
    Defines methods to handle the cache of the settings (they should
    not change a lot) """
Gabriel Detraz's avatar
Gabriel Detraz committed
49 50
    @classmethod
    def set_in_cache(cls):
51
        """ Save the preferences in a server-side cache """
Gabriel Detraz's avatar
Gabriel Detraz committed
52 53 54 55 56 57
        instance, _created = cls.objects.get_or_create()
        cache.set(cls().__class__.__name__.lower(), instance, None)
        return instance

    @classmethod
    def get_cached_value(cls, key):
58
        """ Get the preferences from the server-side cache """
Gabriel Detraz's avatar
Gabriel Detraz committed
59
        instance = cache.get(cls().__class__.__name__.lower())
Maël Kervella's avatar
Maël Kervella committed
60
        if instance is None:
61
            instance = cls.set_in_cache()
Gabriel Detraz's avatar
Gabriel Detraz committed
62 63 64 65 66 67
        return getattr(instance, key)

    class Meta:
        abstract = True


chirac's avatar
chirac committed
68
class OptionalUser(AclMixin, PreferencesModel):
69 70
    """Options pour l'user : obligation ou nom du telephone,
    activation ou non du solde, autorisation du negatif, fingerprint etc"""
71

72 73
    is_tel_mandatory = models.BooleanField(default=True)
    gpg_fingerprint = models.BooleanField(default=True)
74
    all_can_create_club = models.BooleanField(
75
        default=False,
76
        help_text=_("Users can create a club")
77 78 79
    )
    all_can_create_adherent = models.BooleanField(
        default=False,
80
        help_text=_("Users can create a member"),
81
    )
82

Gabriel Detraz's avatar
Gabriel Detraz committed
83 84 85 86 87 88
    shell_default = models.OneToOneField(
        'users.ListShell',
        on_delete=models.PROTECT,
        blank=True,
        null=True
    )
89 90
    self_change_shell = models.BooleanField(
        default=False,
91
        help_text=_("Users can edit their shell")
92
    )
93 94 95 96
    self_change_room = models.BooleanField(
        default=False,
        help_text=_("Users can edit their room")
    )
97
    local_email_accounts_enabled = models.BooleanField(
chirac's avatar
chirac committed
98
        default=False,
99
        help_text=_("Enable local email accounts for users")
chirac's avatar
chirac committed
100
    )
101
    local_email_domain = models.CharField(
102 103 104
        max_length=32,
        default="@example.org",
        help_text=_("Domain to use for local email accounts")
105
    )
106
    max_email_address = models.IntegerField(
107 108 109
        default=15,
        help_text=_("Maximum number of local email addresses for a standard"
                    " user")
grisel-davy's avatar
grisel-davy committed
110
    )
111 112 113 114
    delete_notyetactive = models.IntegerField(
        default=15,
        help_text=_("Inactive users will be deleted after this number of days")
    )
115 116 117 118
    self_adhesion = models.BooleanField(
        default=False,
        help_text=_("A new user can create their account on Re2o")
    )
119

120 121
    class Meta:
        permissions = (
122
            ("view_optionaluser", _("Can view the user options")),
123
        )
124
        verbose_name = _("user options")
125

126
    def clean(self):
Gabriel Detraz's avatar
Gabriel Detraz committed
127 128
        """Clean model:
        Check the mail_extension
129
        """
130
        if self.local_email_domain[0] != "@":
131
            raise ValidationError(_("Email domain must begin with @"))
132

133

134
@receiver(post_save, sender=OptionalUser)
135
def optionaluser_post_save(**kwargs):
136
    """Ecriture dans le cache"""
137 138 139 140
    user_pref = kwargs['instance']
    user_pref.set_in_cache()


chirac's avatar
chirac committed
141
class OptionalMachine(AclMixin, PreferencesModel):
142 143
    """Options pour les machines : maximum de machines ou d'alias par user
    sans droit, activation de l'ipv6"""
144

145 146 147 148
    SLAAC = 'SLAAC'
    DHCPV6 = 'DHCPV6'
    DISABLED = 'DISABLED'
    CHOICE_IPV6 = (
149 150 151
        (SLAAC, _("Autoconfiguration by RA")),
        (DHCPV6, _("IP addresses assigning by DHCPv6")),
        (DISABLED, _("Disabled")),
152 153
    )

154 155 156
    password_machine = models.BooleanField(default=False)
    max_lambdauser_interfaces = models.IntegerField(default=10)
    max_lambdauser_aliases = models.IntegerField(default=10)
157 158 159 160 161
    ipv6_mode = models.CharField(
        max_length=32,
        choices=CHOICE_IPV6,
        default='DISABLED'
    )
162
    create_machine = models.BooleanField(
163
        default=True
164
    )
165 166 167

    @cached_property
    def ipv6(self):
168
        """ Check if the IPv6 option is activated """
Maël Kervella's avatar
Maël Kervella committed
169
        return not self.get_cached_value('ipv6_mode') == 'DISABLED'
170

171 172
    class Meta:
        permissions = (
173
            ("view_optionalmachine", _("Can view the machine options")),
174
        )
175
        verbose_name = _("machine options")
176

177

178
@receiver(post_save, sender=OptionalMachine)
179
def optionalmachine_post_save(**kwargs):
180
    """Synchronisation ipv6 et ecriture dans le cache"""
181
    machine_pref = kwargs['instance']
182
    machine_pref.set_in_cache()
183 184 185 186 187
    if machine_pref.ipv6_mode != "DISABLED":
        for interface in machines.models.Interface.objects.all():
            interface.sync_ipv6()


chirac's avatar
chirac committed
188
class OptionalTopologie(AclMixin, PreferencesModel):
189 190
    """Reglages pour la topologie : mode d'accès radius, vlan où placer
    les machines en accept ou reject"""
191 192 193
    MACHINE = 'MACHINE'
    DEFINED = 'DEFINED'
    CHOICE_RADIUS = (
194 195
        (MACHINE, _("On the IP range's VLAN of the machine")),
        (DEFINED, _("Preset in 'VLAN for machines accepted by RADIUS'")),
196
    )
Gabriel Detraz's avatar
Gabriel Detraz committed
197 198 199 200
    CHOICE_PROVISION = (
        ('sftp', 'sftp'),
        ('tftp', 'tftp'),
    )
201

202 203 204 205 206 207 208
    switchs_web_management = models.BooleanField(
        default=False,
        help_text="Web management, activé si provision automatique"
    )
    switchs_web_management_ssl = models.BooleanField(
        default=False,
        help_text="Web management ssl. Assurez-vous que un certif est installé sur le switch !"
209
    )
210 211 212 213 214 215 216 217 218 219 220
    switchs_rest_management = models.BooleanField(
        default=False,
        help_text="Rest management, activé si provision auto"
    )
    switchs_ip_type = models.OneToOneField(
        'machines.IpType',
        on_delete=models.PROTECT,
        blank=True,
        null=True,
        help_text="Plage d'ip de management des switchs"
    )
Gabriel Detraz's avatar
Gabriel Detraz committed
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
    switchs_provision = models.CharField(
        max_length=32,
        choices=CHOICE_PROVISION,
        default='tftp',
        help_text="Mode de récupération des confs par les switchs"
    )
    sftp_login = models.CharField(
        max_length=32,
        null=True,
        blank=True,
        help_text="Login sftp des switchs"
    )
    sftp_pass = AESEncryptedField(
        max_length=63,
        null=True,
        blank=True,
        help_text="Mot de passe sftp"
    )
239 240 241

    @cached_property
    def provisioned_switchs(self):
242
        """Liste des switches provisionnés"""
243
        from topologie.models import Switch
Gabriel Detraz's avatar
Gabriel Detraz committed
244
        return Switch.objects.filter(automatic_provision=True).order_by('interface__domain__name')
245

246
    @cached_property
247 248 249 250 251 252 253 254
    def switchs_management_interface(self):
        """Return the ip of the interface that the switch have to contact to get it's config"""
        if self.switchs_ip_type:
            from machines.models import Role, Interface
            return Interface.objects.filter(machine__interface__in=Role.interface_for_roletype("switch-conf-server")).filter(type__ip_type=self.switchs_ip_type).first()
        else:
            return None

255
    @cached_property
256 257 258 259 260 261
    def switchs_management_interface_ip(self):
        """Same, but return the ipv4"""
        if not self.switchs_management_interface:
            return None
        return self.switchs_management_interface.ipv4

Gabriel Detraz's avatar
Gabriel Detraz committed
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
    @cached_property
    def switchs_management_sftp_creds(self):
        """Credentials des switchs pour provion sftp"""
        if self.sftp_login and self.sftp_pass:
            return {'login' : self.sftp_login, 'pass' : self.sftp_pass}
        else:
            return None

    @cached_property
    def switchs_management_utils(self):
        """Used for switch_conf, return a list of ip on vlans"""
        from machines.models import Role, Ipv6List, Interface
        def return_ips_dict(interfaces):
            return {'ipv4' : [str(interface.ipv4) for interface in interfaces], 'ipv6' : Ipv6List.objects.filter(interface__in=interfaces).values_list('ipv6', flat=True)}

        ntp_servers = Role.all_interfaces_for_roletype("ntp-server").filter(type__ip_type=self.switchs_ip_type)
        log_servers = Role.all_interfaces_for_roletype("log-server").filter(type__ip_type=self.switchs_ip_type)
        radius_servers = Role.all_interfaces_for_roletype("radius-server").filter(type__ip_type=self.switchs_ip_type)
        dhcp_servers = Role.all_interfaces_for_roletype("dhcp-server")
klafyvel's avatar
klafyvel committed
281
        dns_recursive_servers = Role.all_interfaces_for_roletype("dns-recursive-server").filter(type__ip_type=self.switchs_ip_type)
Gabriel Detraz's avatar
Gabriel Detraz committed
282 283 284 285 286
        subnet = None
        subnet6 = None
        if self.switchs_ip_type:
            subnet = self.switchs_ip_type.ip_set_full_info
            subnet6 = self.switchs_ip_type.ip6_set_full_info
287
        return {'ntp_servers': return_ips_dict(ntp_servers), 'log_servers': return_ips_dict(log_servers), 'radius_servers': return_ips_dict(radius_servers), 'dhcp_servers': return_ips_dict(dhcp_servers), 'dns_recursive_servers': return_ips_dict(dns_recursive_servers), 'subnet': subnet, 'subnet6': subnet6}
Gabriel Detraz's avatar
Gabriel Detraz committed
288

289 290 291 292
    @cached_property
    def provision_switchs_enabled(self):
        """Return true if all settings are ok : switchs on automatic provision,
        ip_type"""
Gabriel Detraz's avatar
Gabriel Detraz committed
293
        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))
294 295
    class Meta:
        permissions = (
296
            ("view_optionaltopologie", _("Can view the topology options")),
297
        )
298
        verbose_name = _("topology options")
299

300

301
@receiver(post_save, sender=OptionalTopologie)
302
def optionaltopologie_post_save(**kwargs):
303
    """Ecriture dans le cache"""
304 305 306 307
    topologie_pref = kwargs['instance']
    topologie_pref.set_in_cache()


308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
class RadiusKey(AclMixin, models.Model):
    """Class of a radius key"""
    radius_key = AESEncryptedField(
        max_length=255,
        help_text="Clef radius"
    )
    comment = models.CharField(
        max_length=255,
        null=True,
        blank=True,
        help_text="Commentaire de cette clef"
    )
    default_switch = models.BooleanField(
        default=True,
        unique=True,
        help_text= "Clef par défaut des switchs"
    )

    class Meta:
        permissions = (
            ("view_radiuskey", "Peut voir un objet radiuskey"),
        )

331 332 333
    def __str__(self):
        return "Clef radius " + str(self.id) + " " + str(self.comment)

334

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
class SwitchManagementCred(AclMixin, models.Model):
    """Class of a management creds of a switch, for rest management"""
    management_id = models.CharField(
        max_length=63,
        help_text="Login du switch"
    )
    management_pass = AESEncryptedField(
        max_length=63,
        help_text="Mot de passe"
    )
    default_switch = models.BooleanField(
        default=True,
        unique=True,
        help_text= "Creds par défaut des switchs"
    )

    class Meta:
        permissions = (
            ("view_switchmanagementcred", "Peut voir un objet switchmanagementcred"),
        )

    def __str__(self):
        return "Identifiant " + str(self.management_id)


360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
class Reminder(AclMixin, models.Model):
    """Options pour les mails de notification de fin d'adhésion.
    Days: liste des nombres de jours pour lesquells un mail est envoyé
    optionalMessage: message additionel pour le mail
    """
    PRETTY_NAME="Options pour le mail de fin d'adhésion"

    days = models.IntegerField(
        default=7,
        unique=True,
        help_text="Délais entre le mail et la fin d'adhésion"
    )
    message = models.CharField(
        max_length=255,
        default="",
        null=True,
        blank=True,
        help_text="Message affiché spécifiquement pour ce rappel"
    )

    class Meta:
        permissions = (
            ("view_reminder", "Peut voir un objet reminder"),
        )

    def users_to_remind(self):
        from re2o.utils import all_has_access
        date = timezone.now().replace(minute=0,hour=0)
        futur_date = date + timedelta(days=self.days)
389
        users = all_has_access(futur_date).exclude(pk__in = all_has_access(futur_date + timedelta(days=1)))
390 391 392
        return users


chirac's avatar
chirac committed
393
class GeneralOption(AclMixin, PreferencesModel):
394 395
    """Options générales : nombre de resultats par page, nom du site,
    temps où les liens sont valides"""
396

397
    general_message_fr = models.TextField(
398 399
        default="",
        blank=True,
400 401
        help_text=_("General message displayed on the French version of the"
                    " website (e.g. in case of maintenance)")
402 403 404 405
    )
    general_message_en = models.TextField(
        default="",
        blank=True,
406 407
        help_text=_("General message displayed on the English version of the"
                    " website (e.g. in case of maintenance)")
408
    )
409 410 411
    search_display_page = models.IntegerField(default=15)
    pagination_number = models.IntegerField(default=25)
    pagination_large_number = models.IntegerField(default=8)
412 413
    req_expire_hrs = models.IntegerField(default=48)
    site_name = models.CharField(max_length=32, default="Re2o")
414
    email_from = models.EmailField(default="www-data@example.com")
415
    main_site_url = models.URLField(max_length=255, default="http://re2o.example.org")
416 417 418 419 420
    GTU_sum_up = models.TextField(
        default="",
        blank=True,
    )
    GTU = models.FileField(
Maël Kervella's avatar
Maël Kervella committed
421
        upload_to='',
422 423 424 425
        default="",
        null=True,
        blank=True,
    )
426

427 428
    class Meta:
        permissions = (
429
            ("view_generaloption", _("Can view the general options")),
430
        )
431
        verbose_name = _("general options")
432

433

434
@receiver(post_save, sender=GeneralOption)
435
def generaloption_post_save(**kwargs):
436
    """Ecriture dans le cache"""
437 438 439 440
    general_pref = kwargs['instance']
    general_pref.set_in_cache()


chirac's avatar
chirac committed
441
class Service(AclMixin, models.Model):
442 443
    """Liste des services affichés sur la page d'accueil : url, description,
    image et nom"""
444 445 446
    name = models.CharField(max_length=32)
    url = models.URLField()
    description = models.TextField()
447
    image = models.ImageField(upload_to='logo', blank=True)
448

449 450
    class Meta:
        permissions = (
451
            ("view_service", _("Can view the service options")),
452
        )
453 454
        verbose_name = _("service")
        verbose_name_plural =_("services")
455

456 457 458
    def __str__(self):
        return str(self.name)

459
class MailContact(AclMixin, models.Model):
460
    """Contact email adress with a commentary."""
461 462 463

    address = models.EmailField(
        default = "contact@example.org",
464
        help_text = _("Contact email address")
465 466 467 468 469
    )

    commentary = models.CharField(
        blank = True,
        null = True,
470
        help_text = _(
471
            "Description of the associated email address."),
472 473 474
        max_length = 256
    )

475 476 477 478
    @cached_property
    def get_name(self):
        return self.address.split("@")[0]

479 480
    class Meta:
        permissions = (
481
            ("view_mailcontact", _("Can view a contact email address object")),
482
        )
483 484
        verbose_name = _("contact email address")
        verbose_name_plural = _("contact email addresses")
485 486 487 488

    def __str__(self):
        return(self.address)

489

chirac's avatar
chirac committed
490
class AssoOption(AclMixin, PreferencesModel):
491
    """Options générales de l'asso : siret, addresse, nom, etc"""
492

493
    name = models.CharField(
494
        default=_("Networking organisation school Something"),
495 496
        max_length=256
    )
497
    siret = models.CharField(default="00000000000000", max_length=32)
498 499
    adresse1 = models.CharField(default=_("Threadneedle Street"), max_length=128)
    adresse2 = models.CharField(default=_("London EC2R 8AH"), max_length=128)
500 501
    contact = models.EmailField(default="contact@example.org")
    telephone = models.CharField(max_length=15, default="0000000000")
502
    pseudo = models.CharField(default=_("Organisation"), max_length=32)
503 504 505 506 507 508
    utilisateur_asso = models.OneToOneField(
        'users.User',
        on_delete=models.PROTECT,
        blank=True,
        null=True
    )
Gabriel Detraz's avatar
Gabriel Detraz committed
509 510 511 512
    description = models.TextField(
        null=True,
        blank=True,
    )
513

514 515
    class Meta:
        permissions = (
516
            ("view_assooption", _("Can view the organisation options")),
517
        )
518
        verbose_name = _("organisation options")
519

520

521
@receiver(post_save, sender=AssoOption)
522
def assooption_post_save(**kwargs):
523
    """Ecriture dans le cache"""
524 525 526 527
    asso_pref = kwargs['instance']
    asso_pref.set_in_cache()


Gabriel Detraz's avatar
Gabriel Detraz committed
528 529
class HomeOption(AclMixin, PreferencesModel):
    """Settings of the home page (facebook/twitter etc)"""
Gabriel Detraz's avatar
Gabriel Detraz committed
530 531 532

    facebook_url = models.URLField(
        null=True,
533
        blank=True
Gabriel Detraz's avatar
Gabriel Detraz committed
534 535 536
    )
    twitter_url = models.URLField(
        null=True,
537
        blank=True
Gabriel Detraz's avatar
Gabriel Detraz committed
538 539 540 541
    )
    twitter_account_name = models.CharField(
        max_length=32,
        null=True,
542
        blank=True
Gabriel Detraz's avatar
Gabriel Detraz committed
543 544 545 546
    )

    class Meta:
        permissions = (
547
            ("view_homeoption", _("Can view the homepage options")),
Gabriel Detraz's avatar
Gabriel Detraz committed
548
        )
549
        verbose_name = _("homepage options")
Gabriel Detraz's avatar
Gabriel Detraz committed
550 551


Gabriel Detraz's avatar
Gabriel Detraz committed
552 553
@receiver(post_save, sender=HomeOption)
def homeoption_post_save(**kwargs):
Gabriel Detraz's avatar
Gabriel Detraz committed
554
    """Ecriture dans le cache"""
Gabriel Detraz's avatar
Gabriel Detraz committed
555 556
    home_pref = kwargs['instance']
    home_pref.set_in_cache()
Gabriel Detraz's avatar
Gabriel Detraz committed
557 558


chirac's avatar
chirac committed
559
class MailMessageOption(AclMixin, models.Model):
560
    """Reglages, mail de bienvenue et autre"""
561

562 563
    welcome_mail_fr = models.TextField(default="", help_text="Mail de bienvenue en français")
    welcome_mail_en = models.TextField(default="", help_text="Mail de bienvenue en anglais")
564

565 566
    class Meta:
        permissions = (
567 568
            ("view_mailmessageoption", _("Can view the email message"
                                         " options")),
569
        )
570 571
        verbose_name = _("email message options")

Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
572

573
class RadiusOption(AclMixin, PreferencesModel):
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
574 575 576 577 578 579 580 581 582
    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'")),
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
583 584 585 586 587 588
    REJECT = 'REJECT'
    SET_VLAN = 'SET_VLAN'
    CHOICE_POLICY = (
        (REJECT, _('Reject the machine')),
        (SET_VLAN, _('Place the machine on the VLAN'))
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
589 590 591 592 593
    radius_general_policy = models.CharField(
        max_length=32,
        choices=CHOICE_RADIUS,
        default='DEFINED'
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
594 595 596 597
    unknown_machine = models.CharField(
        max_length=32,
        choices=CHOICE_POLICY,
        default=REJECT,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
598 599
        verbose_name=_("Policy for unknown machines"),
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
600 601
    unknown_machine_vlan = models.ForeignKey(
        'machines.Vlan',
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
602
        on_delete=models.PROTECT,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
603 604 605 606 607 608 609 610 611 612 613 614
        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,
615
        verbose_name=_("Policy for unknown port"),
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
616
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
617 618
    unknown_port_vlan = models.ForeignKey(
        'machines.Vlan',
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
619
        on_delete=models.PROTECT,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
620 621 622 623 624 625 626 627 628 629 630 631
        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,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
632 633 634 635 636 637
        verbose_name=_(
            "Policy for machine connecting from "
            "unregistered room (relevant on ports with STRICT "
            "radius mode)"
        ),
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
638 639 640
    unknown_room_vlan = models.ForeignKey(
        'machines.Vlan',
        related_name='unknown_room_vlan',
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
641
        on_delete=models.PROTECT,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
642 643 644 645 646 647 648 649 650 651 652
        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,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
653 654
        verbose_name=_("Policy non member users."),
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
655 656 657
    non_member_vlan = models.ForeignKey(
        'machines.Vlan',
        related_name='non_member_vlan',
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
658
        on_delete=models.PROTECT,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
659 660 661 662 663 664 665 666 667 668 669
        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,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
670
        verbose_name=_("Policy for banned users."),
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
671 672 673 674 675 676 677 678 679 680 681
    )
    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.'
        )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
682 683 684 685 686 687 688 689 690
    )
    vlan_decision_ok = models.OneToOneField(
        'machines.Vlan',
        on_delete=models.PROTECT,
        related_name='vlan_ok_option',
        blank=True,
        null=True
    )