Browse Source

LDAP backend for VPN

Baptiste Jonglez 11 years ago
parent
commit
e9cfdc2cc6
2 changed files with 55 additions and 1 deletions
  1. 3 0
      coin/resources/models.py
  2. 52 1
      coin/vpn/models.py

+ 3 - 0
coin/resources/models.py

@@ -45,6 +45,9 @@ class IPPool(models.Model):
 
 
 
 
 class IPSubnet(models.Model):
 class IPSubnet(models.Model):
+    # TODO: find some way to signal to Subscriptions objects when a subnet
+    # gets modified (so that the subscription can update the LDAP backend
+    # accordingly)
     inet = CidrAddressField(blank=True, validators=[validate_subnet],
     inet = CidrAddressField(blank=True, validators=[validate_subnet],
                             verbose_name="Leave empty for automatic allocation")
                             verbose_name="Leave empty for automatic allocation")
     objects = NetManager()
     objects = NetManager()

+ 52 - 1
coin/vpn/models.py

@@ -1,7 +1,12 @@
+# -*- coding: utf-8 -*-
 from django.db import models
 from django.db import models
 from django.core.exceptions import ValidationError
 from django.core.exceptions import ValidationError
 from netfields import InetAddressField, NetManager
 from netfields import InetAddressField, NetManager
 from netaddr import IPAddress
 from netaddr import IPAddress
+import ldapdb.models
+from ldapdb.models.fields import CharField, IntegerField, ListField
+
+from coin.models import CoinLdapSyncModel
 
 
 
 
 def validate_v4(address):
 def validate_v4(address):
@@ -13,8 +18,9 @@ def validate_v6(address):
         raise ValidationError('{} is not an IPv6 address'.format(address))
         raise ValidationError('{} is not an IPv6 address'.format(address))
 
 
 
 
-class VPNSubscription(models.Model):
+class VPNSubscription(CoinLdapSyncModel):
     administrative_subscription = models.OneToOneField('offers.OfferSubscription')
     administrative_subscription = models.OneToOneField('offers.OfferSubscription')
+    # TODO: do some access control to prevent the user from changing this field
     activated = models.BooleanField(default=False)
     activated = models.BooleanField(default=False)
     login = models.CharField(max_length=50)
     login = models.CharField(max_length=50)
     # TODO: define which hash to use
     # TODO: define which hash to use
@@ -24,6 +30,29 @@ class VPNSubscription(models.Model):
 
 
     objects = NetManager()
     objects = NetManager()
 
 
+    def get_subnets(version):
+        subnets = self.administrative_subscription.ip_subnet.all()
+        return [subnet for subnet in subnets if subnet.inet.version == version]
+
+    def sync_to_ldap(self, creation):
+        if creation:
+            config = LdapVPNConfig()
+        else:
+            config = LdapVPNConfig.objects.get(pk=self.login)
+        config.login = config.sn = self.login
+        # TODO: salt + hash the password
+        config.password = self.password
+        config.active = 'yes' if self.activated else 'no'
+        config.ipv4_endpoint = str(self.ipv4_endpoint)
+        config.ipv6_endpoint = str(self.ipv6_endpoint)
+        config.ranges_v4 = [str(s) for s in self.get_subnets(4)]
+        config.ranges_v4 = [str(s) for s in self.get_subnets(6)]
+        config.save()
+
+    def delete_from_ldap(self):
+        # TODO: simple delete?
+        pass
+
     def clean(self):
     def clean(self):
         # TODO: this should be factored for other technologies (DSL, etc)
         # TODO: this should be factored for other technologies (DSL, etc)
         subnets = self.administrative_subscription.ip_subnet.all()
         subnets = self.administrative_subscription.ip_subnet.all()
@@ -53,3 +82,25 @@ class VPNSubscription(models.Model):
 
 
     def __unicode__(self):
     def __unicode__(self):
         return self.login
         return self.login
+
+
+class LdapVPNConfig(ldapdb.models.Model):
+    # TODO: déplacer ligne suivante dans settings.py
+    base_dn = "ou=vpn,ou=unix,o=ILLYSE,l=Villeurbanne,st=RHA,c=FR"
+    object_classes = ['person', 'organizationalPerson', 'inetOrgPerson',
+                      'top', 'radiusprofile']
+
+    login = CharField(db_column='cn', primary_key=True, max_length=255)
+    sn = CharField(db_column='sn', primary_key=True, max_length=255)
+    password = CharField(db_column='userPassword', max_length=255)
+    active = CharField(db_column='dialupAccess', max_length=3)
+    ipv4_endpoint = CharField(db_column='radiusFramedIPAddress', max_length=16)
+    ipv6_endpoint = CharField(db_column='postalAddress', max_length=40)
+    ranges_v4 = ListField(db_column='radiusFramedRoute')
+    ranges_v6 = ListField(db_column='registeredAddress')
+
+    def __unicode__(self):
+        return self.login
+
+    class Meta:
+        managed = False  # Indique à South de ne pas gérer le model LdapUser