diff --git a/attributs.py b/attributs.py index 428c031..da4adb2 100644 --- a/attributs.py +++ b/attributs.py @@ -37,6 +37,7 @@ import re import sys +import ssl import netaddr import time import base64 @@ -252,6 +253,7 @@ class Attr(object): #: Le nom de l'attribut dans le schéma LDAP ldap_name = None python_type = None + binary = False """La liste des droits qui suffisent à avoir le droit de modifier la valeur""" can_modify = [nounou] @@ -389,8 +391,8 @@ class objectClass(Attr): def parse_value(self, val): if val not in [ 'top', 'organizationalUnit', 'posixAccount', 'shadowAccount', 'proprio', 'adherent', 'club', 'machine', 'machineCrans', - 'borneWifi', 'machineWifi', 'machineFixe', - 'cransAccount', 'service', 'facture', 'freeMid' ]: + 'borneWifi', 'machineWifi', 'machineFixe', 'x509Cert', 'TLSACert', + 'baseCert', 'cransAccount', 'service', 'facture', 'freeMid' ]: raise ValueError("Pourquoi insérer un objectClass=%r ?" % val) else: self.value = unicode(val) @@ -401,7 +403,7 @@ class intAttr(Attr): python_type = int def parse_value(self, val): - if self.python_type(val) <= 0: + if self.python_type(val) < 0: raise ValueError("Valeur entière invalide : %r" % val) self.value = self.python_type(val) @@ -1434,3 +1436,74 @@ class rewriteMailHeaders(boolAttr): @crans_attribute class machineAlias(boolAttr): ldap_name = "machineAlias" + +@crans_attribute +class issuerCN(Attr): + ldap_name = "issuerCN" + +@crans_attribute +class serialNumber(Attr): + ldap_name = "serialNumber" + +@crans_attribute +class start(intAttr): + ldap_name = "start" + +@crans_attribute +class end(intAttr): + ldap_name = "end" + +@crans_attribute +class crlUrl(Attr): + ldap_name = "crlUrl" + optional = True + +@crans_attribute +class revocked(boolAttr): + ldap_name = "revocked" + singlevalue = True + optional = True + +@crans_attribute +class certificat(Attr): + ldap_name = "certificat" + binary = True + python_type = str + def __unicode__(self): + return unicode(ssl.DER_cert_to_PEM_cert(self.value)) + def __str__(self): + return self.value + +@crans_attribute +class certificatUsage(intAttr): + ldap_name = "certificatUsage" + singlevalue = True + +@crans_attribute +class selector(intAttr): + ldap_name = "selector" + singlevalue = True + +@crans_attribute +class matchingType(intAttr): + ldap_name = "matchingType" + singlevalue = True + +@crans_attribute +class xid(intAttr): + ldap_name = "xid" + category = 'id' + unique = True + singlevalue = True + + +@crans_attribute +class hostCert(dnsAttr): + optional = False + can_modify = [parent, nounou] + ldap_name = "hostCert" + + def parse_value(self, host): + if not host in self.parent.machine()['host'] + self.parent.machine()['hostAlias']: + raise ValueError("hostCert doit être inclus dans les host et hostAlias de la machine parente : %s" % ', '.join(self.parent.machine()['host'] + self.parent.machine()['hostAlias'])) + self.value = host diff --git a/lc_ldap.py b/lc_ldap.py index 9b52a6c..6641bd8 100644 --- a/lc_ldap.py +++ b/lc_ldap.py @@ -329,8 +329,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): raise EnvironmentError("Vous n'avez pas le droit de créer cet adhérent.") def newFacture(self, parent, fldif): - """Crée une nouvelle facture - --Non implémenté !""" + """Crée une nouvelle facture""" uldif = copy.deepcopy(fldif) # fid uldif['fid'] = [ unicode(self._find_id('fid')) ] @@ -341,6 +340,18 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): else: raise EnvironmentError("Vous n'avez pas le droit de créer cette facture.") + def newCertificat(self, parent, xldif): + """Crée un nouveau certificat x509""" + uldif = copy.deepcopy(xldif) + # xid + uldif['xid'] = [ unicode(self._find_id('xid')) ] + uldif['objectClass'] = [u'baseCert'] + baseCert = self._create_entity('xid=%s,%s' % (uldif['xid'][0], parent), uldif) + if baseCert.may_be(variables.created, self.droits + self._check_parent(baseCert.dn)): + return baseCert + else: + raise EnvironmentError("Vous n'avez pas le droit de créer ce certiticat.") + def _create_entity(self, dn, uldif): '''Crée une nouvelle entité ldap avec le dn ``dn`` et les attributs de ``ldif``. Attention, ldif doit contenir des @@ -393,7 +404,10 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): raise EnvironmentError('Aucun %s libre dans la plage [%d, %d]' % (attr, plage[0], i)) else: - i = nonfree[-1] + 1 + try: + i = nonfree[-1] + 1 + except IndexError: + i = 1 while True: try: self.lockholder.addlock(attr, str(i)) diff --git a/objets.py b/objets.py index 70417e0..8798c61 100644 --- a/objets.py +++ b/objets.py @@ -297,7 +297,10 @@ class CransLdapObject(object): # On nettoie les locks for key, values in self._modifs.to_ldif().iteritems(): for value in values: - self.conn.lockholder.removelock(key, value) + try: + self.conn.lockholder.removelock(key, value) + except: + pass self.conn.lockholder.purge(id(self)) # Services à relancer @@ -705,6 +708,7 @@ class machine(CransLdapObject): def __init__(self, conn, dn, mode='ro', ldif = None): super(machine, self).__init__(conn, dn, mode, ldif) self._proprio = None + self._certificats = None def proprio(self, mode=None): u"""Renvoie le propriétaire de la machine""" @@ -713,6 +717,14 @@ class machine(CransLdapObject): self._proprio = new_cransldapobject(self.conn, parent_dn, self.mode if mode is None else mode) return self._proprio + def certificats(self): + """Renvoie la liste des certificats de la machine""" + if self._certificats is None: + self._certificats = self.conn.search(u'xid=*', dn = self.dn, scope = 1, mode=self.mode) + for m in self._certificats: + m._machine = self + return self._certificats + def blacklist_actif(self, excepts=[]): u"""Renvoie la liste des blacklistes actives sur la machine et le proprio""" black=self.proprio().blacklist_actif(excepts) @@ -954,6 +966,10 @@ class machineMulticast(machine): pass def ressuscite(self, comm, login): pass + def proprio(self, mode=None): + return None + def certificats(self): + return [] @crans_object class machineWifi(machine): @@ -1048,6 +1064,52 @@ class facture(CransLdapObject): self._proprio = new_cransldapobject(self.conn, parent_dn, self.mode) return self._proprio +@crans_object +class baseCert(CransLdapObject): + can_be_by = { variables.created: [attributs.nounou, attributs.bureau], + variables.modified: [attributs.nounou, attributs.bureau], + variables.deleted: [attributs.nounou, attributs.bureau], + } + attribs = [ attributs.xid, attributs.certificat, attributs.hostCert, attributs.historique] + + tlsa_attribs = [ attributs.certificatUsage, attributs.selector, attributs.matchingType, + attributs.portTCPin, attributs.portUDPin] + x509_attribs = [ attributs.issuerCN, attributs.start, attributs.end, + attributs.crlUrl, attributs.revocked, attributs.serialNumber ] + + ldap_name = "baseCert" + + _machine = None + + def __init__(self, conn, dn, mode='ro', ldif=None): + super(baseCert, self).__init__(conn, dn, mode, ldif) + if "TLSACert" in self['objectClass']: + self.attribs.extend(self.tlsa_attribs) + if 'x509Cert' in self['objectClass']: + self.attribs.extend(self.x509_attribs) + + def tlsa(self, certificatUsage, matchingType): + if not self.mode in ['w', 'rw']: + return + if u"TLSACert" in self['objectClass']: + return + self._modifs['objectClass'].append(u"TLSACert") + self.attribs.extend(self.tlsa_attribs) + self['certificatUsage']=certificatUsage + self['matchingType']=matchingType + self['selector']=0 + + def x509(issuerCN, start, end, serialNumber, crlUrl=None): + pass + + def machine(self): + u"""Renvoie la machine du certificat""" + parent_dn = self.dn.split(',', 1)[1] + if not self._machine: + self._machine = new_cransldapobject(self.conn, parent_dn, self.mode) + return self._machine + + @crans_object class service(CransLdapObject): ldap_name = "service" diff --git a/services.py b/services.py index f853439..8288adb 100644 --- a/services.py +++ b/services.py @@ -36,7 +36,7 @@ services_to_objects['delete']['del_user'] = [objets.adherent, objets.club] def services_to_args_mail_modif(x): if isinstance(x, attributs.droits): return [ "uid=%s" % x.parent['uid'][0] ] - elif isinstance(x, attributs.Attr) and x.__class__ in [ attributs.exempt ] + services_to_attrs['ports']: + elif isinstance(x, attributs.Attr) and x.__class__ in [ attributs.exempt ] + services_to_attrs['ports'] and isinstance(x.parent, objets.machine): return [ "mid=%s" % x.parent['mid'][0] ] elif isinstance(x.parent, objets.machine) and isinstance(x.parent.proprio(mode='ro'), objets.AssociationCrans): return [ "mid=%s" % x.parent['mid'][0] ]