Browse Source

Merge branch 'master' into generic_isp

Jocelyn Delande 10 years ago
parent
commit
8a6c1185bb

+ 4 - 2
coin/members/admin.py

@@ -49,7 +49,8 @@ class MemberAdmin(UserAdmin):
             ('status', 'resign_date'),
             'type',
             ('first_name', 'last_name', 'nickname'),
-            'organization_name')}),
+            'organization_name',
+            'comments')}),
         ('Coordonnées', {'fields': (
             'email',
             ('home_phone_number', 'mobile_phone_number'),
@@ -66,7 +67,8 @@ class MemberAdmin(UserAdmin):
             'status',
             'type',
             ('first_name', 'last_name', 'nickname'),
-            'organization_name')}),
+            'organization_name',
+            'comments')}),
         ('Coordonnées', {'fields': (
             'email',
             ('home_phone_number', 'mobile_phone_number'),

+ 0 - 0
coin/members/management/__init__.py


+ 0 - 0
coin/members/management/commands/__init__.py


+ 15 - 0
coin/members/management/commands/members_email.py

@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.core.management.base import BaseCommand, CommandError
+
+from coin.members.models import Member
+
+
+class Command(BaseCommand):
+    help = 'Returns the email addresses of all members, in a format suitable for bulk importing in Sympa'
+
+    def handle(self, *args, **options):
+        emails = [m.email for m in Member.objects.filter(status='member')]
+        for email in emails:
+            self.stdout.write(email)

+ 20 - 0
coin/members/migrations/0011_member_comments.py

@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('members', '0010_auto_20141008_2246'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='member',
+            name='comments',
+            field=models.TextField(default='', help_text="Commentaires libres (informations sp\xe9cifiques concernant l'adh\xe9sion, raison du d\xe9part, etc)", verbose_name='commentaires', blank=True),
+            preserve_default=False,
+        ),
+    ]

+ 32 - 3
coin/members/models.py

@@ -62,11 +62,14 @@ class Member(CoinLdapSyncMixin, AbstractUser):
     country = models.CharField(max_length=200, blank=True, null=True,
                                default='France',
                                verbose_name='pays')
-
     resign_date = models.DateField(null=True, blank=True,
                                    verbose_name="date de départ de "
                                    "l'association",
                                    help_text="En cas de départ prématuré")
+    comments = models.TextField(blank=True, verbose_name='commentaires',
+                                help_text="Commentaires libres (informations"
+                                " spécifiques concernant l'adhésion,"
+                                " raison du départ, etc)")
 
     # Following fields are managed by the parent class AbstractUser :
     # username, first_name, last_name, email
@@ -146,6 +149,18 @@ class Member(CoinLdapSyncMixin, AbstractUser):
             Q(subscription_date__gt=date) |
             Q(resign_date__lt=date))
 
+    def get_ssh_keys(self):
+        # Quick & dirty, ensure that keys are unique (otherwise, LDAP complains)
+        return list({k.key for k in self.cryptokey_set.filter(type='RSA')})
+
+    def sync_ssh_keys(self):
+        """
+        Called whenever a SSH key is saved
+        """
+        ldap_user = LdapUser.objects.get(pk=self.username)
+        ldap_user.sshPublicKey = self.get_ssh_keys()
+        ldap_user.save()
+
     def sync_to_ldap(self, creation, update_fields, *args, **kwargs):
         """
         Update LDAP data when a member is saved
@@ -186,6 +201,7 @@ class Member(CoinLdapSyncMixin, AbstractUser):
             ldap_user.uid = self.username
             ldap_user.nick_name = self.username
             ldap_user.uidNumber = uid_number
+            ldap_user.homeDirectory = '/home/' + self.username
 
         if self.type == 'natural_person':
             ldap_user.last_name = self.last_name
@@ -199,6 +215,9 @@ class Member(CoinLdapSyncMixin, AbstractUser):
             # Make sure password is hashed
             ldap_user.password = utils.ldap_hash(self._password_ldap)
 
+        # Store SSH keys
+        ldap_user.sshPublicKey = self.get_ssh_keys()
+
         ldap_user.save()
 
         # if creation:
@@ -296,7 +315,7 @@ def get_automatic_username(member):
     return username
 
 
-class CryptoKey(models.Model):
+class CryptoKey(CoinLdapSyncMixin, models.Model):
 
     KEY_TYPE_CHOICES = (('RSA', 'RSA'), ('GPG', 'GPG'))
 
@@ -305,6 +324,13 @@ class CryptoKey(models.Model):
     key = models.TextField(verbose_name='clé')
     member = models.ForeignKey('Member', verbose_name='membre')
 
+    def sync_to_ldap(self, creation, *args, **kwargs):
+        """Simply tell the member object to resync all its SSH keys to LDAP"""
+        self.member.sync_ssh_keys()
+
+    def delete_from_ldap(self, *args, **kwargs):
+        self.member.sync_ssh_keys()
+
     def __unicode__(self):
         return 'Clé %s de %s' % (self.type, self.member)
 
@@ -360,7 +386,7 @@ class LdapUser(ldapdb.models.Model):
     # "ou=users,ou=unix,o=ILLYSE,l=Villeurbanne,st=RHA,c=FR"
     base_dn = settings.LDAP_USER_BASE_DN
     object_classes = [b'inetOrgPerson', b'organizationalPerson', b'person',
-                      b'top', b'posixAccount']
+                      b'top', b'posixAccount', b'ldapPublicKey']
 
     uid = CharField(db_column=b'uid', unique=True, max_length=255)
     nick_name = CharField(db_column=b'cn', unique=True, primary_key=True,
@@ -374,6 +400,9 @@ class LdapUser(ldapdb.models.Model):
     gidNumber = IntegerField(db_column=b'gidNumber', default=2000)
     homeDirectory = CharField(db_column=b'homeDirectory', max_length=255,
                               default='/tmp')
+    loginShell = CharField(db_column=b'loginShell', max_length=255,
+                              default='/bin/bash')
+    sshPublicKey = ListField(db_column=b'sshPublicKey', default=[])
 
     def __unicode__(self):
         return self.display_name

+ 5 - 1
coin/utils.py

@@ -106,7 +106,11 @@ def start_of_month():
 
 
 def end_of_month():
-    return date(date.today().year, date.today().month + 1, 1) - timedelta(days=1)
+    today = date.today()
+    if today.month == 12:
+        return date(today.year + 1, 1, 1) - timedelta(days=1)
+    else:
+        return date(today.year, today.month + 1, 1) - timedelta(days=1)
 
 if __name__ == '__main__':
     print(ldap_hash('coin'))

+ 3 - 3
coin/vpn/admin.py

@@ -23,9 +23,9 @@ class VPNConfigurationAdmin(ConfigurationAdminFormMixin, PolymorphicChildModelAd
     list_filter = ('activated',)
     search_fields = ('login', 'comment',
                      # TODO: searching on member directly doesn't work
-                     'administrative_subscription__member__first_name',
-                     'administrative_subscription__member__last_name',
-                     'administrative_subscription__member__email')
+                     'offersubscription__member__first_name',
+                     'offersubscription__member__last_name',
+                     'offersubscription__member__email')
     actions = (delete_selected, "generate_endpoints", "generate_endpoints_v4",
                "generate_endpoints_v6", "activate", "deactivate")
     exclude = ("password",)

+ 9 - 17
coin/vpn/views.py

@@ -27,24 +27,12 @@ class VPNView(SuccessMessageMixin, UpdateView):
         return super(VPNView, self).dispatch(*args, **kwargs)
 
     def get_object(self):
+        if self.request.user.is_superuser:
+            return get_object_or_404(VPNConfiguration, pk=self.kwargs.get("id"))
+        # For normal users, ensure the VPN belongs to them.
         return get_object_or_404(VPNConfiguration, pk=self.kwargs.get("id"),
                                  offersubscription__member=self.request.user)
 
-def generate_password(request, id):
-    """This generates a random password, saves it in hashed form, and returns
-    it to the user in cleartext.
-    """
-    vpn = get_object_or_404(VPNConfiguration, pk=id,
-                            offersubscription__member=request.user)
-    # This function has nothing to here, but it's convenient.
-    password = Member.objects.make_random_password()
-    vpn.password = password
-    # This will hash the password automatically
-    vpn.full_clean()
-    vpn.save()
-    return render_to_response('vpn/fragments/password.html', {"vpn": vpn,
-                                                    "password": password})
-
 
 class VPNGeneratePasswordView(VPNView):
     """This generates a random password, saves it in hashed form, and returns
@@ -66,8 +54,12 @@ class VPNGeneratePasswordView(VPNView):
 def get_graph(request, vpn_id, period="daily"):
     """ This get the graph for the associated vpn_id and time period
     """
-    vpn = get_object_or_404(VPNConfiguration, pk=vpn_id,
-                            offersubscription__member=request.user)
+    if request.user.is_superuser:
+        vpn = get_object_or_404(VPNConfiguration, pk=vpn_id)
+    else:
+        # For normal users, ensure the VPN belongs to them
+        vpn = get_object_or_404(VPNConfiguration, pk=vpn_id,
+                                offersubscription__member=request.user)
 
     time_periods = { 'hourly': '-1hour', 'daily': '-24hours', 'weekly': '-8days', 'monthly': '-32days', 'yearly': '-13months', }
     if period not in time_periods: