models.py 11.5 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.db import models
30
from django.db.models.signals import post_save
31
from django.dispatch import receiver
32
from django.core.cache import cache
33
from django.forms import ValidationError
34
from django.utils.translation import ugettext_lazy as _
35

36
import machines.models
chirac's avatar
chirac committed
37
from re2o.mixins import AclMixin
38

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

Gabriel Detraz's avatar
Gabriel Detraz committed
40
class PreferencesModel(models.Model):
41
42
43
    """ 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
44
45
    @classmethod
    def set_in_cache(cls):
46
        """ Save the preferences in a server-side cache """
Gabriel Detraz's avatar
Gabriel Detraz committed
47
48
49
50
51
52
        instance, _created = cls.objects.get_or_create()
        cache.set(cls().__class__.__name__.lower(), instance, None)
        return instance

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

    class Meta:
        abstract = True


chirac's avatar
chirac committed
63
class OptionalUser(AclMixin, PreferencesModel):
64
65
    """Options pour l'user : obligation ou nom du telephone,
    activation ou non du solde, autorisation du negatif, fingerprint etc"""
66
67
    PRETTY_NAME = "Options utilisateur"

68
69
    is_tel_mandatory = models.BooleanField(default=True)
    gpg_fingerprint = models.BooleanField(default=True)
70
    all_can_create_club = models.BooleanField(
71
        default=False,
72
73
74
75
76
        help_text="Les users peuvent créer un club"
    )
    all_can_create_adherent = models.BooleanField(
        default=False,
        help_text="Les users peuvent créer d'autres adhérents",
77
    )
78
79
80
81
    self_adhesion = models.BooleanField(
        default=False,
        help_text="Un nouvel utilisateur peut se créer son compte sur re2o"
    )
Gabriel Detraz's avatar
Gabriel Detraz committed
82
83
84
85
86
87
    shell_default = models.OneToOneField(
        'users.ListShell',
        on_delete=models.PROTECT,
        blank=True,
        null=True
    )
88
    local_email_accounts_enabled = models.BooleanField(
chirac's avatar
chirac committed
89
        default=False,
90
        help_text="Enable local email accounts for users"
chirac's avatar
chirac committed
91
    )
92
    local_email_domain = models.CharField(
93
94
        max_length = 32,
        default = "@example.org",
95
        help_text="Domain to use for local email accounts",
96
    )
97
    max_email_address = models.IntegerField(
grisel-davy's avatar
grisel-davy committed
98
        default = 15,
99
        help_text = "Maximum number of local email address for a standard user"
grisel-davy's avatar
grisel-davy committed
100
    )
101

102
103
104
105
106
    class Meta:
        permissions = (
            ("view_optionaluser", "Peut voir les options de l'user"),
        )

107
    def clean(self):
Gabriel Detraz's avatar
Gabriel Detraz committed
108
109
        """Clean model:
        Check the mail_extension
110
        """
111
112
113
        if self.local_email_domain[0] != "@":
            raise ValidationError("Mail domain must begin with @")

114

115
@receiver(post_save, sender=OptionalUser)
116
def optionaluser_post_save(**kwargs):
117
    """Ecriture dans le cache"""
118
119
120
121
    user_pref = kwargs['instance']
    user_pref.set_in_cache()


chirac's avatar
chirac committed
122
class OptionalMachine(AclMixin, PreferencesModel):
123
124
    """Options pour les machines : maximum de machines ou d'alias par user
    sans droit, activation de l'ipv6"""
125
126
    PRETTY_NAME = "Options machines"

127
128
129
130
131
132
133
134
135
    SLAAC = 'SLAAC'
    DHCPV6 = 'DHCPV6'
    DISABLED = 'DISABLED'
    CHOICE_IPV6 = (
        (SLAAC, 'Autoconfiguration par RA'),
        (DHCPV6, 'Attribution des ip par dhcpv6'),
        (DISABLED, 'Désactivé'),
    )

136
137
138
    password_machine = models.BooleanField(default=False)
    max_lambdauser_interfaces = models.IntegerField(default=10)
    max_lambdauser_aliases = models.IntegerField(default=10)
139
140
141
142
143
    ipv6_mode = models.CharField(
        max_length=32,
        choices=CHOICE_IPV6,
        default='DISABLED'
    )
144
145
146
147
    create_machine = models.BooleanField(
        default=True,
        help_text="Permet à l'user de créer une machine"
    )
148
149
150

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

154
155
156
157
158
    class Meta:
        permissions = (
            ("view_optionalmachine", "Peut voir les options de machine"),
        )

159

160
@receiver(post_save, sender=OptionalMachine)
161
def optionalmachine_post_save(**kwargs):
162
    """Synchronisation ipv6 et ecriture dans le cache"""
163
    machine_pref = kwargs['instance']
164
    machine_pref.set_in_cache()
165
166
167
168
169
    if machine_pref.ipv6_mode != "DISABLED":
        for interface in machines.models.Interface.objects.all():
            interface.sync_ipv6()


chirac's avatar
chirac committed
170
class OptionalTopologie(AclMixin, PreferencesModel):
171
172
    """Reglages pour la topologie : mode d'accès radius, vlan où placer
    les machines en accept ou reject"""
173
    PRETTY_NAME = "Options topologie"
174
175
176
    MACHINE = 'MACHINE'
    DEFINED = 'DEFINED'
    CHOICE_RADIUS = (
177
178
179
        (MACHINE, 'Sur le vlan de la plage ip machine'),
        (DEFINED, 'Prédéfini dans "Vlan où placer les machines\
            après acceptation RADIUS"'),
180
    )
181

182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
    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
    )
201

202
203
204
205
206
    class Meta:
        permissions = (
            ("view_optionaltopologie", "Peut voir les options de topologie"),
        )

207

208
@receiver(post_save, sender=OptionalTopologie)
209
def optionaltopologie_post_save(**kwargs):
210
    """Ecriture dans le cache"""
211
212
213
214
    topologie_pref = kwargs['instance']
    topologie_pref.set_in_cache()


chirac's avatar
chirac committed
215
class GeneralOption(AclMixin, PreferencesModel):
216
217
    """Options générales : nombre de resultats par page, nom du site,
    temps où les liens sont valides"""
218
219
    PRETTY_NAME = "Options générales"

220
221
222
223
224
    general_message = models.TextField(
        default="",
        blank=True,
        help_text="Message général affiché sur le site (maintenance, etc"
    )
225
226
227
    search_display_page = models.IntegerField(default=15)
    pagination_number = models.IntegerField(default=25)
    pagination_large_number = models.IntegerField(default=8)
228
229
    req_expire_hrs = models.IntegerField(default=48)
    site_name = models.CharField(max_length=32, default="Re2o")
230
    email_from = models.EmailField(default="www-data@example.com")
231
232
233
234
235
    GTU_sum_up = models.TextField(
        default="",
        blank=True,
    )
    GTU = models.FileField(
Maël Kervella's avatar
Maël Kervella committed
236
        upload_to='',
237
238
239
240
        default="",
        null=True,
        blank=True,
    )
241

242
243
244
245
246
    class Meta:
        permissions = (
            ("view_generaloption", "Peut voir les options générales"),
        )

247

248
@receiver(post_save, sender=GeneralOption)
249
def generaloption_post_save(**kwargs):
250
    """Ecriture dans le cache"""
251
252
253
254
    general_pref = kwargs['instance']
    general_pref.set_in_cache()


chirac's avatar
chirac committed
255
class Service(AclMixin, models.Model):
256
257
    """Liste des services affichés sur la page d'accueil : url, description,
    image et nom"""
258
259
260
    name = models.CharField(max_length=32)
    url = models.URLField()
    description = models.TextField()
261
    image = models.ImageField(upload_to='logo', blank=True)
262

263
264
265
266
267
    class Meta:
        permissions = (
            ("view_service", "Peut voir les options de service"),
        )

268
269
270
    def __str__(self):
        return str(self.name)

271
class MailContact(AclMixin, models.Model):
272
    """Contact email adress with a commentary."""
273
274
275

    address = models.EmailField(
        default = "contact@example.org",
276
        help_text = _("Contact email adress")
277
278
279
280
281
    )

    commentary = models.CharField(
        blank = True,
        null = True,
282
283
        help_text = _(
            "Description of the associated email adress."),
284
285
286
        max_length = 256
    )

287
288
289
290
    @cached_property
    def get_name(self):
        return self.address.split("@")[0]

291
292
    class Meta:
        permissions = (
293
            ("view_mailcontact", _("Can see contact email")),
294
295
296
297
298
        )

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

299

chirac's avatar
chirac committed
300
class AssoOption(AclMixin, PreferencesModel):
301
    """Options générales de l'asso : siret, addresse, nom, etc"""
302
303
    PRETTY_NAME = "Options de l'association"

304
305
306
307
    name = models.CharField(
        default="Association réseau école machin",
        max_length=256
    )
308
309
310
311
312
313
    siret = models.CharField(default="00000000000000", max_length=32)
    adresse1 = models.CharField(default="1 Rue de exemple", max_length=128)
    adresse2 = models.CharField(default="94230 Cachan", max_length=128)
    contact = models.EmailField(default="contact@example.org")
    telephone = models.CharField(max_length=15, default="0000000000")
    pseudo = models.CharField(default="Asso", max_length=32)
314
315
316
317
318
319
    utilisateur_asso = models.OneToOneField(
        'users.User',
        on_delete=models.PROTECT,
        blank=True,
        null=True
    )
Gabriel Detraz's avatar
Gabriel Detraz committed
320
321
322
323
    description = models.TextField(
        null=True,
        blank=True,
    )
324

325
326
327
328
329
    class Meta:
        permissions = (
            ("view_assooption", "Peut voir les options de l'asso"),
        )

330

331
@receiver(post_save, sender=AssoOption)
332
def assooption_post_save(**kwargs):
333
    """Ecriture dans le cache"""
334
335
336
337
    asso_pref = kwargs['instance']
    asso_pref.set_in_cache()


Gabriel Detraz's avatar
Gabriel Detraz committed
338
339
class HomeOption(AclMixin, PreferencesModel):
    """Settings of the home page (facebook/twitter etc)"""
Gabriel Detraz's avatar
Gabriel Detraz committed
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
    PRETTY_NAME = "Options de la page d'accueil"

    facebook_url = models.URLField(
        null=True,
        blank=True,
        help_text="Url du compte facebook"
    )
    twitter_url = models.URLField(
        null=True,
        blank=True,
        help_text="Url du compte twitter"
    )
    twitter_account_name = models.CharField(
        max_length=32,
        null=True,
        blank=True,
        help_text="Nom du compte à afficher"
    )

    class Meta:
        permissions = (
Gabriel Detraz's avatar
Gabriel Detraz committed
361
            ("view_homeoption", "Peut voir les options de l'accueil"),
Gabriel Detraz's avatar
Gabriel Detraz committed
362
363
364
        )


Gabriel Detraz's avatar
Gabriel Detraz committed
365
366
@receiver(post_save, sender=HomeOption)
def homeoption_post_save(**kwargs):
Gabriel Detraz's avatar
Gabriel Detraz committed
367
    """Ecriture dans le cache"""
Gabriel Detraz's avatar
Gabriel Detraz committed
368
369
    home_pref = kwargs['instance']
    home_pref.set_in_cache()
Gabriel Detraz's avatar
Gabriel Detraz committed
370
371


chirac's avatar
chirac committed
372
class MailMessageOption(AclMixin, models.Model):
373
    """Reglages, mail de bienvenue et autre"""
374
375
376
377
    PRETTY_NAME = "Options de corps de mail"

    welcome_mail_fr = models.TextField(default="")
    welcome_mail_en = models.TextField(default="")
378

379
380
381
382
    class Meta:
        permissions = (
            ("view_mailmessageoption", "Peut voir les options de mail"),
        )