models.py 23.2 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

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
    self_change_room = models.BooleanField(
        default=False,
95
        help_text=_("Users can edit their room.")
96
    )
97
    local_email_accounts_enabled = models.BooleanField(
98
        default=False,
99
        help_text=_("Enable local email accounts for users.")
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
        default=15,
        help_text=_("Maximum number of local email addresses for a standard"
109
                    " user.")
110
    )
111 112
    delete_notyetactive = models.IntegerField(
        default=15,
113 114
        help_text=_("Not yet active users will be deleted after this number of"
                    " days.")
115
    )
116 117
    self_adhesion = models.BooleanField(
        default=False,
118
        help_text=_("A new user can create their account on Re2o.")
119
    )
120 121
    all_users_active = models.BooleanField(
        default=False,
122 123
        help_text=_("If True, all new created and connected users are active."
                    " If False, only when a valid registration has been paid.")
124
    )
125

126 127
    class Meta:
        permissions = (
128
            ("view_optionaluser", _("Can view the user options")),
129
        )
130
        verbose_name = _("user options")
131

132
    def clean(self):
Gabriel Detraz's avatar
Gabriel Detraz committed
133 134
        """Clean model:
        Check the mail_extension
135
        """
136
        if self.local_email_domain[0] != "@":
137
            raise ValidationError(_("Email domain must begin with @"))
138

139

140
@receiver(post_save, sender=OptionalUser)
141
def optionaluser_post_save(**kwargs):
142
    """Ecriture dans le cache"""
143 144 145 146
    user_pref = kwargs['instance']
    user_pref.set_in_cache()


chirac's avatar
chirac committed
147
class OptionalMachine(AclMixin, PreferencesModel):
148 149
    """Options pour les machines : maximum de machines ou d'alias par user
    sans droit, activation de l'ipv6"""
150

151 152 153 154
    SLAAC = 'SLAAC'
    DHCPV6 = 'DHCPV6'
    DISABLED = 'DISABLED'
    CHOICE_IPV6 = (
155 156 157
        (SLAAC, _("Autoconfiguration by RA")),
        (DHCPV6, _("IP addresses assigning by DHCPv6")),
        (DISABLED, _("Disabled")),
158 159
    )

160 161 162
    password_machine = models.BooleanField(default=False)
    max_lambdauser_interfaces = models.IntegerField(default=10)
    max_lambdauser_aliases = models.IntegerField(default=10)
163 164 165 166 167
    ipv6_mode = models.CharField(
        max_length=32,
        choices=CHOICE_IPV6,
        default='DISABLED'
    )
168
    create_machine = models.BooleanField(
169
        default=True
170
    )
171 172 173

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

177 178
    class Meta:
        permissions = (
179
            ("view_optionalmachine", _("Can view the machine options")),
180
        )
181
        verbose_name = _("machine options")
182

183

184
@receiver(post_save, sender=OptionalMachine)
185
def optionalmachine_post_save(**kwargs):
186
    """Synchronisation ipv6 et ecriture dans le cache"""
187
    machine_pref = kwargs['instance']
188
    machine_pref.set_in_cache()
189 190 191 192 193
    if machine_pref.ipv6_mode != "DISABLED":
        for interface in machines.models.Interface.objects.all():
            interface.sync_ipv6()


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

208 209
    switchs_web_management = models.BooleanField(
        default=False,
210
        help_text=_("Web management, activated in case of automatic provision")
211 212 213
    )
    switchs_web_management_ssl = models.BooleanField(
        default=False,
214 215
        help_text=_("SSL web management, make sure that a certificate is"
                    " installed on the switch")
216
    )
217 218
    switchs_rest_management = models.BooleanField(
        default=False,
219
        help_text=_("REST management, activated in case of automatic provision")
220 221 222 223 224 225
    )
    switchs_ip_type = models.OneToOneField(
        'machines.IpType',
        on_delete=models.PROTECT,
        blank=True,
        null=True,
226
        help_text=_("IP range for the management of switches")
227
    )
Gabriel Detraz's avatar
Gabriel Detraz committed
228 229 230 231
    switchs_provision = models.CharField(
        max_length=32,
        choices=CHOICE_PROVISION,
        default='tftp',
232
        help_text=_("Provision of configuration mode for switches")
Gabriel Detraz's avatar
Gabriel Detraz committed
233 234 235 236 237
    )
    sftp_login = models.CharField(
        max_length=32,
        null=True,
        blank=True,
238
        help_text=_("SFTP login for switches")
Gabriel Detraz's avatar
Gabriel Detraz committed
239 240 241 242 243
    )
    sftp_pass = AESEncryptedField(
        max_length=63,
        null=True,
        blank=True,
244
        help_text=_("SFTP password")
Gabriel Detraz's avatar
Gabriel Detraz committed
245
    )
246 247 248

    @cached_property
    def provisioned_switchs(self):
249
        """Liste des switches provisionnés"""
250
        from topologie.models import Switch
251
        return Switch.objects.filter(automatic_provision=True).order_by('interface__domain__name')
252

253
    @cached_property
254 255 256 257 258 259 260 261
    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

262
    @cached_property
263 264 265 266 267 268
    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
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
    @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")
288
        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
289 290 291 292 293
        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
294
        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
295

296 297 298 299
    @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
300
        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))
301 302
    class Meta:
        permissions = (
303
            ("view_optionaltopologie", _("Can view the topology options")),
304
        )
305
        verbose_name = _("topology options")
306

307

308
@receiver(post_save, sender=OptionalTopologie)
309
def optionaltopologie_post_save(**kwargs):
310
    """Ecriture dans le cache"""
311 312 313 314
    topologie_pref = kwargs['instance']
    topologie_pref.set_in_cache()


315 316 317 318
class RadiusKey(AclMixin, models.Model):
    """Class of a radius key"""
    radius_key = AESEncryptedField(
        max_length=255,
319
        help_text=_("RADIUS key")
320 321 322 323 324
    )
    comment = models.CharField(
        max_length=255,
        null=True,
        blank=True,
325
        help_text=_("Comment for this key")
326 327 328 329
    )
    default_switch = models.BooleanField(
        default=True,
        unique=True,
330
        help_text=_("Default key for switches")
331 332 333 334
    )

    class Meta:
        permissions = (
335
            ("view_radiuskey", _("Can view a RADIUS key object")),
336
        )
337 338
        verbose_name = _("RADIUS key")
        verbose_name_plural = _("RADIUS keys")
339

340
    def __str__(self):
341
        return _("RADIUS key ") + str(self.id) + " " + str(self.comment)
342

343

344 345 346 347
class SwitchManagementCred(AclMixin, models.Model):
    """Class of a management creds of a switch, for rest management"""
    management_id = models.CharField(
        max_length=63,
348
        help_text=_("Switch login")
349 350 351
    )
    management_pass = AESEncryptedField(
        max_length=63,
352
        help_text=_("Password")
353 354 355 356
    )
    default_switch = models.BooleanField(
        default=True,
        unique=True,
357
        help_text=_("Default credentials for switches")
358 359 360 361
    )

    class Meta:
        permissions = (
362 363
            ("view_switchmanagementcred", _("Can view a switch management"
                                            " credentials object")),
364
        )
365
        verbose_name = _("switch management credentials")
366 367

    def __str__(self):
368
        return _("Switch login ") + str(self.management_id)
369 370


371 372 373 374 375 376 377 378 379
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
    """

    days = models.IntegerField(
        default=7,
        unique=True,
380
        help_text=_("Delay between the email and the membership's end")
381 382 383 384 385 386
    )
    message = models.CharField(
        max_length=255,
        default="",
        null=True,
        blank=True,
387
        help_text=_("Message displayed specifically for this reminder")
388 389 390 391
    )

    class Meta:
        permissions = (
392
            ("view_reminder", _("Can view a reminder object")),
393
        )
394 395
        verbose_name = _("reminder")
        verbose_name_plural = _("reminders")
396 397 398 399 400

    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)
401
        users = all_has_access(futur_date).exclude(pk__in = all_has_access(futur_date + timedelta(days=1)))
402 403 404
        return users


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

409
    general_message_fr = models.TextField(
410 411
        default="",
        blank=True,
412 413
        help_text=_("General message displayed on the French version of the"
                    " website (e.g. in case of maintenance)")
414 415 416 417
    )
    general_message_en = models.TextField(
        default="",
        blank=True,
418 419
        help_text=_("General message displayed on the English version of the"
                    " website (e.g. in case of maintenance)")
420
    )
421 422 423
    search_display_page = models.IntegerField(default=15)
    pagination_number = models.IntegerField(default=25)
    pagination_large_number = models.IntegerField(default=8)
424 425
    req_expire_hrs = models.IntegerField(default=48)
    site_name = models.CharField(max_length=32, default="Re2o")
426
    email_from = models.EmailField(default="www-data@example.com")
427
    main_site_url = models.URLField(max_length=255, default="http://re2o.example.org")
428 429 430 431 432
    GTU_sum_up = models.TextField(
        default="",
        blank=True,
    )
    GTU = models.FileField(
Maël Kervella's avatar
Maël Kervella committed
433
        upload_to='',
434 435 436 437
        default="",
        null=True,
        blank=True,
    )
438

439 440
    class Meta:
        permissions = (
441
            ("view_generaloption", _("Can view the general options")),
442
        )
443
        verbose_name = _("general options")
444

445

446
@receiver(post_save, sender=GeneralOption)
447
def generaloption_post_save(**kwargs):
448
    """Ecriture dans le cache"""
449 450 451 452
    general_pref = kwargs['instance']
    general_pref.set_in_cache()


chirac's avatar
chirac committed
453
class Service(AclMixin, models.Model):
454 455
    """Liste des services affichés sur la page d'accueil : url, description,
    image et nom"""
456 457 458
    name = models.CharField(max_length=32)
    url = models.URLField()
    description = models.TextField()
459
    image = models.ImageField(upload_to='logo', blank=True)
460

461 462
    class Meta:
        permissions = (
463
            ("view_service", _("Can view the service options")),
464
        )
465 466
        verbose_name = _("service")
        verbose_name_plural =_("services")
467

468 469 470
    def __str__(self):
        return str(self.name)

471
class MailContact(AclMixin, models.Model):
472
    """Contact email adress with a commentary."""
473 474 475

    address = models.EmailField(
        default = "contact@example.org",
476
        help_text = _("Contact email address")
477 478 479 480 481
    )

    commentary = models.CharField(
        blank = True,
        null = True,
482
        help_text = _("Description of the associated email address."),
483 484 485
        max_length = 256
    )

486 487 488 489
    @cached_property
    def get_name(self):
        return self.address.split("@")[0]

490 491
    class Meta:
        permissions = (
492
            ("view_mailcontact", _("Can view a contact email address object")),
493
        )
494 495
        verbose_name = _("contact email address")
        verbose_name_plural = _("contact email addresses")
496 497 498 499

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

500

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

504
    name = models.CharField(
505
        default=_("Networking organisation school Something"),
506 507
        max_length=256
    )
508
    siret = models.CharField(default="00000000000000", max_length=32)
509 510
    adresse1 = models.CharField(default=_("Threadneedle Street"), max_length=128)
    adresse2 = models.CharField(default=_("London EC2R 8AH"), max_length=128)
511 512
    contact = models.EmailField(default="contact@example.org")
    telephone = models.CharField(max_length=15, default="0000000000")
513
    pseudo = models.CharField(default=_("Organisation"), max_length=32)
514 515 516 517 518 519
    utilisateur_asso = models.OneToOneField(
        'users.User',
        on_delete=models.PROTECT,
        blank=True,
        null=True
    )
520 521 522 523
    description = models.TextField(
        null=True,
        blank=True,
    )
524 525 526 527 528 529
    pres_name = models.CharField(
        max_length=255,
        default="",
        verbose_name=_("President of the association"),
        help_text=_("Displayed on subscription vouchers")
    )
530

531 532
    class Meta:
        permissions = (
533
            ("view_assooption", _("Can view the organisation options")),
534
        )
535
        verbose_name = _("organisation options")
536

537

538
@receiver(post_save, sender=AssoOption)
539
def assooption_post_save(**kwargs):
540
    """Ecriture dans le cache"""
541 542 543 544
    asso_pref = kwargs['instance']
    asso_pref.set_in_cache()


Gabriel Detraz's avatar
Gabriel Detraz committed
545 546
class HomeOption(AclMixin, PreferencesModel):
    """Settings of the home page (facebook/twitter etc)"""
Gabriel Detraz's avatar
Gabriel Detraz committed
547 548 549

    facebook_url = models.URLField(
        null=True,
550
        blank=True
Gabriel Detraz's avatar
Gabriel Detraz committed
551 552 553
    )
    twitter_url = models.URLField(
        null=True,
554
        blank=True
Gabriel Detraz's avatar
Gabriel Detraz committed
555 556 557 558
    )
    twitter_account_name = models.CharField(
        max_length=32,
        null=True,
559
        blank=True
Gabriel Detraz's avatar
Gabriel Detraz committed
560 561 562 563
    )

    class Meta:
        permissions = (
564
            ("view_homeoption", _("Can view the homepage options")),
Gabriel Detraz's avatar
Gabriel Detraz committed
565
        )
566
        verbose_name = _("homepage options")
Gabriel Detraz's avatar
Gabriel Detraz committed
567 568


Gabriel Detraz's avatar
Gabriel Detraz committed
569 570
@receiver(post_save, sender=HomeOption)
def homeoption_post_save(**kwargs):
Gabriel Detraz's avatar
Gabriel Detraz committed
571
    """Ecriture dans le cache"""
Gabriel Detraz's avatar
Gabriel Detraz committed
572 573
    home_pref = kwargs['instance']
    home_pref.set_in_cache()
Gabriel Detraz's avatar
Gabriel Detraz committed
574 575


chirac's avatar
chirac committed
576
class MailMessageOption(AclMixin, models.Model):
577
    """Reglages, mail de bienvenue et autre"""
578

579 580
    welcome_mail_fr = models.TextField(default="", help_text=_("Welcome email in French"))
    welcome_mail_en = models.TextField(default="", help_text=_("Welcome email in English"))
581

582 583
    class Meta:
        permissions = (
584 585
            ("view_mailmessageoption", _("Can view the email message"
                                         " options")),
586
        )
587 588
        verbose_name = _("email message options")

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

590
class RadiusOption(AclMixin, PreferencesModel):
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
591
    class Meta:
592 593
        verbose_name = _("RADIUS policy")
        verbose_name_plural = _("RADIUS policies")
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
594 595 596 597 598 599 600

    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
601 602 603
    REJECT = 'REJECT'
    SET_VLAN = 'SET_VLAN'
    CHOICE_POLICY = (
604 605
        (REJECT, _("Reject the machine")),
        (SET_VLAN, _("Place the machine on the VLAN"))
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
606
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
607 608 609 610 611
    radius_general_policy = models.CharField(
        max_length=32,
        choices=CHOICE_RADIUS,
        default='DEFINED'
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
612 613 614 615
    unknown_machine = models.CharField(
        max_length=32,
        choices=CHOICE_POLICY,
        default=REJECT,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
616 617
        verbose_name=_("Policy for unknown machines"),
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
618 619
    unknown_machine_vlan = models.ForeignKey(
        'machines.Vlan',
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
620
        on_delete=models.PROTECT,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
621 622 623
        related_name='unknown_machine_vlan',
        blank=True,
        null=True,
624 625
        verbose_name=_("Unknown machines VLAN"),
        help_text=_("VLAN for unknown machines if not rejected")
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
626 627 628 629 630
    )
    unknown_port = models.CharField(
        max_length=32,
        choices=CHOICE_POLICY,
        default=REJECT,
631
        verbose_name=_("Policy for unknown ports"),
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
632
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
633 634
    unknown_port_vlan = models.ForeignKey(
        'machines.Vlan',
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
635
        on_delete=models.PROTECT,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
636 637 638
        related_name='unknown_port_vlan',
        blank=True,
        null=True,
639 640
        verbose_name=_("Unknown ports VLAN"),
        help_text=_("VLAN for unknown ports if not rejected")
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
641 642 643 644 645
    )
    unknown_room = models.CharField(
        max_length=32,
        choices=CHOICE_POLICY,
        default=REJECT,
646 647
        verbose_name=_("Policy for machines connecting from unregistered rooms"
                       " (relevant on ports with STRICT RADIUS mode)"),
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
648
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
649 650 651
    unknown_room_vlan = models.ForeignKey(
        'machines.Vlan',
        related_name='unknown_room_vlan',
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
652
        on_delete=models.PROTECT,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
653 654
        blank=True,
        null=True,
655 656
        verbose_name=_("Unknown rooms VLAN"),
        help_text=_("VLAN for unknown rooms if not rejected")
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
657 658 659 660 661
    )
    non_member = models.CharField(
        max_length=32,
        choices=CHOICE_POLICY,
        default=REJECT,
662
        verbose_name=_("Policy for non members"),
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
663
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
664 665 666
    non_member_vlan = models.ForeignKey(
        'machines.Vlan',
        related_name='non_member_vlan',
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
667
        on_delete=models.PROTECT,
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
668 669
        blank=True,
        null=True,
670 671
        verbose_name=_("Non members VLAN"),
        help_text=_("VLAN for non members if not rejected")
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
672 673 674 675 676
    )
    banned = models.CharField(
        max_length=32,
        choices=CHOICE_POLICY,
        default=REJECT,
677
        verbose_name=_("Policy for banned users"),
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
678 679 680 681 682 683 684
    )
    banned_vlan = models.ForeignKey(
        'machines.Vlan',
        related_name='banned_vlan',
        on_delete=models.PROTECT,
        blank=True,
        null=True,
685 686
        verbose_name=_("Banned users VLAN"),
        help_text=_("VLAN for banned users if not rejected")
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
687 688 689 690 691 692 693 694 695
    )
    vlan_decision_ok = models.OneToOneField(
        'machines.Vlan',
        on_delete=models.PROTECT,
        related_name='vlan_ok_option',
        blank=True,
        null=True
    )

696 697 698 699 700 701 702 703 704 705 706

class CotisationsOption(AclMixin, PreferencesModel):
    class Meta:
        verbose_name = _("cotisations options")

    invoice_template = models.OneToOneField(
        'cotisations.DocumentTemplate',
        verbose_name=_("Template for invoices"),
        related_name="invoice_template",
        on_delete=models.PROTECT,
    )
Hugo LEVY-FALK's avatar
Hugo LEVY-FALK committed
707 708 709 710 711 712
    voucher_template = models.OneToOneField(
        'cotisations.DocumentTemplate',
        verbose_name=_("Template for subscription voucher"),
        related_name="voucher_template",
        on_delete=models.PROTECT,
    )