#!/usr/bin/env python # -*- coding: utf-8 -*- # """ Définition des classes permettant d'instancier les objets LDAP. """ # # Copyright (C) 2010-2013 Cr@ns # Authors: Antoine Durand-Gasselin # Nicolas Dandrimont # Olivier Iffrig # Valentin Samir # Daniel Stan # Vincent Le Gallic # Pierre-Elliott Bécue # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the Cr@ns nor the names of its contributors may # be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ## import de la lib standard import os import sys import re import datetime import time import ldap from ldap.modlist import addModlist, modifyModlist ## import locaux import lc_ldap import crans_utils import attributs import ldap_locks import services import variables ## import de /usr/scripts/ if not "/usr/scripts/" in sys.path: sys.path.append('/usr/scripts/') import gestion.config as config from gestion.gen_confs.dhcpd_new import dydhcp #: Champs à ignorer dans l'historique HIST_IGNORE_FIELDS = ["modifiersName", "entryCSN", "modifyTimestamp", "historique"] crans_account_attribs = [attributs.uid, attributs.canonicalAlias, attributs.solde, attributs.contourneGreylist, attributs.derniereConnexion, attributs.homepageAlias, attributs.loginShell, attributs.gecos, attributs.uidNumber, attributs.homeDirectory, attributs.gidNumber, attributs.userPassword, attributs.mailAlias, attributs.cn] def new_cransldapobject(conn, dn, mode='ro', uldif=None): """Crée un objet :py:class:`CransLdapObject` en utilisant la classe correspondant à l'``objectClass`` du ``ldif`` --pour usage interne à la librairie uniquement !""" classe = None if dn == variables.base_dn: classe = AssociationCrans elif dn == variables.invite_dn: classe = BaseInvites elif uldif: classe = ObjectFactory.get(uldif['objectClass'][0]) else: res = conn.search_s(dn, 0) if not res: raise ValueError ('objet inexistant: %s' % dn) _, attrs = res[0] classe = ObjectFactory.get(attrs['objectClass'][0]) return classe(conn, dn, mode, uldif) class CransLdapObject(object): """Classe de base des objets :py:class:`CransLdap`. Cette classe ne devrait pas être utilisée directement.""" """ Qui peut faire quoi ? """ can_be_by = { variables.created: [attributs.nounou], variables.modified: [attributs.nounou], variables.deleted: [attributs.nounou], } attribs = [] def __init__(self, conn, dn, mode='ro', uldif=None): ''' Créée une instance d'un objet Crans (machine, adhérent, etc...) à ce ``dn``, si ``uldif`` est précisé, n'effectue pas de recherche dans la base ldap. ''' if not isinstance(conn, lc_ldap.lc_ldap): raise TypeError("conn doit être une instance de lc_ldap") self.conn = conn self.attrs = attributs.AttrsDict(conn, Parent=self) # Contient un dico ldif qui doit représenter ce qui # est dans la base. On attrify paresseusement au moment où on utilise un attribut self._modifs = None # C'est là qu'on met les modifications self.dn = dn orig = {} if uldif: self.attrs = attributs.AttrsDict(self.conn, uldif, Parent=self) self._modifs = attributs.AttrsDict(self.conn, uldif, Parent=self) elif dn != variables.base_dn: res = self.conn.search_s(dn, 0) if not res: raise ValueError ('objet inexistant: %s' % dn) self.dn, ldif = res[0] # L'objet sortant de la base ldap, on ne fait pas de vérifications sur # l'état des données. uldif = lc_ldap.ldif_to_uldif(ldif) self.attrs = attributs.AttrsDict(self.conn, uldif, Parent=self) self._modifs = attributs.AttrsDict(self.conn, uldif, Parent=self) if mode in ['w', 'rw']: if not self.may_be(variables.modified, self.conn.droits + self.conn._check_parent(dn) + self.conn._check_self(dn)): raise EnvironmentError("Vous n'avez pas le droit de modifier cet objet.") self.mode = mode if mode in ['w', 'rw']: # Vérification que `λv. str(Attr(v))` est bien une projection # C'est-à-dire que si on str(Attr(str(Attr(v)))) on retombe sur str(Attr(v)) oldif = lc_ldap.ldif_to_uldif(self.attrs.to_ldif()) nldif = lc_ldap.ldif_to_uldif(attributs.AttrsDict(self.conn, lc_ldap.ldif_to_uldif(self.attrs.to_ldif()), Parent=self).to_ldif()) for attr, vals in oldif.items(): if nldif[attr] != vals: for v in nldif[attr]: if v in vals: vals.remove(v) nvals = [nldif[attr][vals.index(v)] for v in vals ] raise EnvironmentError("λv. str(Attr(v)) n'est peut-être pas une projection (ie non idempotente):", attr, nvals, vals) def history_add(self, login, chain): """Ajoute une ligne à l'historique de l'objet. ###ATTENTION : C'est un kludge pour pouvoir continuer à faire "comme avant", ### mais on devrait tout recoder pour utiliser l'historique LDAP""" assert isinstance(login, unicode) assert isinstance(chain, unicode) new_line = u"%s, %s : %s" % (time.strftime("%d/%m/%Y %H:%M:%S"), login, chain) # Attention, le __setitem__ est surchargé, mais pas .append sur l'historique self["historique"] = self.get("historique", []) + [new_line] def _check_optionnal(self, comment): """Vérifie que les attributs qui ne sont pas optionnels sont effectivement peuplés.""" objet = self.ldap_name for attribut in self.attribs: if not attribut.optional: nom_attr = attribut.ldap_name if len(self._modifs.get(nom_attr, [])) <= 0: raise attributs.OptionalError("L'objet %s que vous %s doit posséder au moins un attribut %s" % (objet, comment, nom_attr)) def _post_creation(self): """Fonction qui effectue quelques tâches lorsque la création est faite""" pass def _post_deletion(self): """Fonction qui effectue quelques tâches lorsque la création est faite""" pass def create(self): """Crée l'objet dans la base ldap, cette méthode vise à faire en sorte que l'objet se crée lui-même, si celui qui essaye de le modifier a les droits de le faire.""" self._check_optionnal(comment="créez") # Création de la requête LDAP modlist = addModlist(self._modifs.to_ldif()) # Requête LDAP de création de l'objet self.conn.add_s(self.dn, modlist) # On nettoie les locks for key, values in self._modifs.to_ldif().iteritems(): for value in values: self.conn.lockholder.removelock(key, value) self.conn.lockholder.purge(id(self)) # Services à relancer services.services_to_restart(self.conn, {}, self._modifs) self._post_creation() def bury(self, comm, login): """Sauvegarde l'objet dans un fichier dans le cimetière.""" self.history_add(login, u"destruction (%s)" % comm) self.save() # On produit un ldif ldif = u"dn: %s\n" % self.dn for key in self.attrs.keys(): for value in self.attrs[key]: ldif += u"%s: %s\n" % (key, value) file = "%s %s" % (datetime.datetime.now(), self.dn) f = open('/home/cimetiere_lc/%s/%s' % (self['objectClass'][0], file.replace(' ', '_')), 'w') f.write(ldif.encode("UTF-8")) f.close() def delete(self, comm="", login=None): """Supprime l'objet de la base LDAP. Appelle :py:meth:`CransLdapObject.bury`.""" if login is None: login = self.conn.current_login if self.mode not in ['w', 'rw']: raise EnvironmentError("Objet en lecture seule, réessayer en lecture/écriture") if not self.may_be(variables.deleted, self.conn.droits): raise EnvironmentError("Vous n'avez pas le droit de supprimer %s." % self.dn) self.bury(comm, login) self.conn.delete_s(self.dn) self.conn.lockholder.purge(id(self)) self._post_deletion() services.services_to_restart(self.conn, self.attrs, {}) def save(self): """Sauvegarde dans la base les modifications apportées à l'objet. Interne: Vérifie que ``self._modifs`` contient des valeurs correctes et enregistre les modifications.""" if self.mode not in ['w', 'rw']: raise EnvironmentError("Objet en lecture seule, réessayer en lecture/écriture") self._check_optionnal(comment="modifiez") # On récupère la liste des modifications modlist = self.get_modlist() try: self.conn.modify_s(self.dn, modlist) except: raise EnvironmentError("Impossible de modifier l'objet, peut-être n'existe-t-il pas ?") # On programme le redémarrage des services services.services_to_restart(self.conn, self.attrs, self._modifs) # On nettoie les locks self.conn.lockholder.purge(id(self)) # Vérification des modifications old_ldif = self.conn.search_s(self.dn, ldap.SCOPE_BASE)[0][1] old_uldif = lc_ldap.ldif_to_uldif(old_ldif) self.attrs = attributs.AttrsDict(self.conn, old_uldif, Parent=self) differences = [] # On fait les différences entre les deux dicos for attr in set(self.attrs.keys()).union(set(self._modifs.keys())): exp_vals = set([unicode(i) for i in self.attrs.get(attr, [])]) new_vals = set([unicode(i) for i in self._modifs.get(attr, [])]) if exp_vals != new_vals: differences.append({"missing": exp_vals - new_vals, "having": new_vals - exp_vals}) if differences: raise EnvironmentError("Les modifications apportées à l'objet %s n'ont pas été correctement sauvegardées\n%s" % (self.dn, differences)) def may_be(self, what, liste): """Teste si liste peut faire ce qui est dans what, pour what élément de {create, delete, modify}. On passe une liste de droits plutôt que l'objet car il faut ajouter les droits soi et parent. Retourne un booléen """ if set(liste).intersection(self.can_be_by[what]) != set([]): return True else: return False def get_modlist(self): """Renvoie un dictionnaire des modifications apportées à l'objet""" # unicode -> utf-8 ldif = self._modifs.to_ldif() orig_ldif = self.attrs.to_ldif() return modifyModlist(orig_ldif, ldif) def get(self, attr, default): """Renvoie l'attribut demandé ou default si introuvable""" try: return self[attr] except KeyError: return default def __getitem__(self, attr): if self.mode in [ 'w', 'rw' ]: return [ v for v in self._modifs[attr] ] elif self.attrs.has_key(attr): return [ v for v in self.attrs[attr] ] elif self.has_key(attr): return [] raise KeyError(attr) def has_key(self, attr): """Est-ce que notre objet a l'attribut en question ?""" return attr in [attrib.__name__ for attrib in self.attribs] def __setitem__(self, attr, values): """Permet d'affecter des valeurs à l'objet comme s'il était un dictionnaire.""" # Quand on est pas en mode d'écriture, ça plante. if self.mode not in ['w', 'rw']: raise ValueError("Objet en lecture seule") if not self.has_key(attr): raise ValueError("L'objet que vous modifiez n'a pas d'attribut %s" % (attr)) # Les valeurs sont nécessairement stockées en liste if not isinstance(values, list): values = [values] # On génére une liste des attributs, le dictionnaire ldif # sert à permettre les vérifications de cardinalité # (on peut pas utiliser self._modifs, car il ne faut # faire le changement que si on peut) attrs_before_verif = [ attributs.attrify(val, attr, self.conn, Parent=self) for val in values ] if attr in self.attrs.keys(): for attribut in attrs_before_verif: attribut.check_uniqueness([content.value for content in self.attrs[attr]]) # On groupe les attributs précédents, et les nouveaux mixed_attrs = attrs_before_verif + self.attrs[attr] else: mixed_attrs = attrs_before_verif # Si c'est vide, on fait pas de vérifs, on avait une liste # vide avant, puis on en a une nouvelle après. if mixed_attrs: # Tests de droits. if not mixed_attrs[0].is_modifiable(self.conn.droits + self.conn._check_parent(self.dn) + self.conn._check_self(self.dn)): raise EnvironmentError("Vous ne pouvez pas toucher aux attributs de type %r." % (attr)) self._modifs[attr] = attrs_before_verif for attribut in attrs_before_verif: if attribut.unique: self.conn.lockholder.addlock(attr, str(attribut), id(self)) def search_historique(self, ign_fields=HIST_IGNORE_FIELDS): u"""Récupère l'historique l'argument optionnel ign_fields contient la liste des champs à ignorer, HIST_IGNORE_FIELDS par défaut Renvoie une liste de lignes de texte.""" res = self.conn.search_s(variables.log_dn, ldap.SCOPE_SUBTREE, 'reqDN=%s' % self.dn) res.sort(key=(lambda a: a[1]['reqEnd'][0])) out = [] for cn, attrs in res: date = crans_utils.format_ldap_time(attrs['reqEnd'][0]) author = attrs['reqAuthzID'][0] if author == "cn=admin,dc=crans,dc=org": author = u"respbats" else: author = author.split(",", 1)[0] res = self.conn.search(author, scope=ldap.SCOPE_ONELEVEL) if res != []: author = res[0].compte() if attrs['reqType'][0] == variables.deleted: out.append(u"%s : [%s] Suppression" % (date, author)) elif attrs['reqType'][0] == variables.modified: fields = {} for mod in attrs['reqMod']: mod = mod.decode('utf-8') field, change = mod.split(':', 1) if field not in ign_fields: if field in fields: fields[field].append(change) else: fields[field] = [change] mod_list = [] for field in fields: mods = fields[field] mod_list.append(u"%s %s" %(field, ", ".join(mods))) if mod_list != []: out.append(u"%s : [%s] %s" % (date, author, u" ; ".join(mod_list))) return out def blacklist_actif(self): """Renvoie la liste des blacklistes actives sur l'entité Améliorations possibles: - Proposer de filtrer les blacklistes avec un arg supplémentaire ? - Vérifier les blacklistes des machines pour les adhérents ? """ blacklist_liste=[] # blacklistes virtuelle si on est un adhérent pour carte étudiant et chambre invalides if self.__class__.__name__ == "adherent" and self.paiement_ok(): if not config.periode_transitoire and config.bl_carte_et_actif and not self.carte_ok() and not self.sursis_carte(): bl = attributs.blacklist(u'%s$%s$%s$%s' % ('-', '-', 'carte_etudiant', ''), {}, self.conn) blacklist_liste.append(bl) if self['chbre'][0].value == '????': bl = attributs.blacklist(u'%s$%s$%s$%s' % ('-', '-', 'chambre_invalide', ''), {}, self.conn) blacklist_liste.append(bl) attrs = (self.attrs if self.mode not in ["w", "rw"] else self._modifs) blacklist_liste.extend(filter((lambda bl: bl.is_actif()), attrs.get("blacklist",[]))) return blacklist_liste def blacklist(self, sanction, commentaire, debut="now", fin = '-'): """ Blacklistage de la ou de toutes la machines du propriétaire * debut et fin sont le nombre de secondes depuis epoch * pour un début ou fin immédiate mettre now * pour une fin indéterminée mettre '-' Les données sont stockées dans la base sous la forme : debut$fin$sanction$commentaire """ if debut == 'now': debut = int(time.time()) if fin == 'now': fin = int(time.time()) bl = attributs.blacklist(u'%s$%s$%s$%s' % (debut, fin, sanction, commentaire), {}, self.conn) self._modifs.setdefault('blacklist', []).append(bl) class ObjectFactory(object): """Utilisée pour enregistrer toutes les classes servant à instancier un objet LDAP. Elle sert à les récupérer à partir de leur nom LDAP. Cette classe n'est jamais instanciée. """ _classes = {} @classmethod def register(cls, name, classe): """Enregistre l'association ``name`` -> ``classe``""" cls._classes[name] = classe @classmethod def get(cls, name): """Retourne la classe qui a ``name`` pour ``ldap_name``. Pas de fallback, on ne veut pas instancier des objets de manière hasardeuse. """ return cls._classes.get(name) def crans_object(classe): """Pour décorer les classes permettant d'instancier des attributs LDAP, afin de les enregistrer dans :py:class:`ObjectFactory`. """ ObjectFactory.register(classe.ldap_name, classe) return classe class proprio(CransLdapObject): u""" Un propriétaire de machine (adhérent, club…) """ can_be_by = { variables.created: [attributs.nounou, attributs.bureau, attributs.cableur], variables.modified: [attributs.nounou, attributs.bureau, attributs.soi, attributs.cableur], variables.deleted: [attributs.nounou, attributs.bureau], } attribs = [attributs.nom, attributs.chbre, attributs.paiement, attributs.info, attributs.blacklist, attributs.controle, attributs.historique] def __init__(self, conn, dn, mode='ro', ldif = None, machines=[]): super(proprio, self).__init__(conn, dn, mode, ldif) self._machines = machines def sursis_carte(self): for h in self['historique'][::-1]: x = re.match("(.*),.* : .*(paiement\+%s|inscription).*" % (config.ann_scol,), h.value) if x != None: return ((time.time()-time.mktime(time.strptime(x.group(1),'%d/%m/%Y %H:%M'))) <= config.sursis_carte) return False def paiement_ok(self): u"""Renvoie si le propriétaire a payé pour l'année en cours""" if self.dn == variables.base_dn: return True bool_paiement = False try: for paiement in self['paiement']: if paiement.value == config.ann_scol: bool_paiement = True break # Pour la période transitoire année précédente ok if config.periode_transitoire and paiement.value == config.ann_scol -1: bool_paiement = True break except KeyError: pass # Doit-on bloquer en cas de manque de la carte d'etudiant ? # (si période transitoire on ne bloque dans aucun cas) if config.bl_carte_et_definitif and not 'club' in map(lambda x:x.value,self["objectClass"]): bool_carte = False try: for carte in self['carteEtudiant']: if carte.value == config.ann_scol: bool_carte = True except KeyError: pass # Si inscrit depuis moins de config.sursis_carte, on laisse un sursis if not bool_carte and self.sursis_carte(): bool_carte = True return bool_carte and bool_paiement return bool_paiement def carte_ok(self): u"""Renvoie si le propriétaire a donné sa carte pour l'année en cours""" if not self.dn == variables.base_dn and config.bl_carte_et_actif and not 'club' in map(lambda x:x.value,self["objectClass"]): bool_carte = False try: for carte in self['carteEtudiant']: if carte.value == config.ann_scol: bool_carte = True except KeyError: pass return bool_carte return True # XXX - To Delete def update_solde(self, diff, comment=u"", login=None): """Modifie le solde du proprio. diff peut être négatif ou positif.""" if login is None: login = self.conn.current_login assert isinstance(diff, int) or isinstance(diff, float) assert isinstance(comment, unicode) solde = float(self["solde"][0].value) new_solde = solde + diff # On vérifie qu'on ne dépasse par le découvert autorisé if new_solde < config.impression.decouvert: raise ValueError(u"Solde minimal atteint, opération non effectuée.") transaction = u"credit" if diff >=0 else u"debit" new_solde = u"%.2f" % new_solde self.history_add(login, u"%s %.2f Euros [%s]" % (transaction, abs(diff), comment)) self["solde"] = new_solde def machines(self): """Renvoie la liste des machines""" if not self._machines: self._machines = self.conn.search('mid=*', dn = self.dn, scope = 1, mode=self.mode) for m in self._machines: m._proprio = self return self._machines def delete(self, comm="", login=None): """Supprimme l'objet de la base LDAP. En supprimant ses enfants d'abord.""" if login is None: login = self.conn.current_login if self.mode not in ['w', 'rw']: raise EnvironmentError("Objet en lecture seule, réessayer en lecture/écriture") if not self.may_be(variables.deleted, self.conn.droits): raise EnvironmentError("Vous n'avez pas le droit de supprimer %s." % self.dn) for machine in self.machines(): machine.delete(comm, login) self.bury(comm, login) self.conn.delete_s(self.dn) services.services_to_restart(self.conn, self.attrs, {}) class machine(CransLdapObject): u""" Une machine """ can_be_by = { variables.created: [attributs.nounou, attributs.bureau, attributs.cableur, attributs.parent], variables.modified: [attributs.nounou, attributs.bureau, attributs.cableur, attributs.parent], variables.deleted: [attributs.nounou, attributs.bureau, attributs.cableur, attributs.parent], } attribs = [attributs.mid, attributs.macAddress, attributs.host, attributs.rid, attributs.info, attributs.blacklist, attributs.hostAlias, attributs.exempt, attributs.portTCPout, attributs.portTCPin, attributs.portUDPout, attributs.portUDPin, attributs.sshFingerprint, attributs.ipHostNumber, attributs.ip6HostNumber, attributs.historique, attributs.dnsIpv6, attributs.machineAlias] def __init__(self, conn, dn, mode='ro', ldif = None): super(machine, self).__init__(conn, dn, mode, ldif) self._proprio = None def proprio(self): u"""Renvoie le propriétaire de la machine""" parent_dn = self.dn.split(',', 1)[1] if not self._proprio: self._proprio = new_cransldapobject(self.conn, parent_dn, self.mode) return self._proprio def blacklist_actif(self): u"""Renvoie la liste des blacklistes actives sur la machine et le proprio Améliorations possibles: - Proposer de filtrer les blacklistes avec un arg supplémentaire ? - Vérifier les blacklistes des machines pour les adhérents ?""" black=self.proprio().blacklist_actif() attrs = (self.attrs if self.mode not in ["w", "rw"] else self._modifs) black.extend(filter((lambda bl: bl.is_actif()), attrs.get("blacklist",[]))) return black def _post_creation(self): """Fonction qui effectue quelques tâches lorsque la création est faite""" if self._proprio: if self._proprio._machines: self._proprio._machines.append(self) def _post_deletion(self): """Fonction qui effectue quelques tâches lorsque l'on veut effacer""" if self._proprio: if self._proprio._machines: self._proprio._machines.remove(self) class AssociationCrans(proprio): u""" Association crans (propriétaire particulier).""" def save(self): pass def ressuscite(self, comm, login): pass def delete(self, comm, login): pass class BaseInvites(proprio): u"""Un artefact de la base ldap""" def delete(self, comm, login): raise EnvironmentError("Les pauvres invites") pass @crans_object class adherent(proprio): u"""Adhérent crans.""" attribs = proprio.attribs + [attributs.aid, attributs.prenom, attributs.tel, attributs.mail, attributs.mailInvalide, attributs.charteMA, attributs.derniereConnexion, attributs.gpgFingerprint, attributs.carteEtudiant, attributs.droits, attributs.etudes, attributs.postalAddress, attributs.mailExt, attributs.compteWiki, ] ldap_name = "adherent" def __init__(self, conn, dn, mode='ro', ldif = None): super(adherent, self).__init__(conn, dn, mode, ldif) self.full = False if u'cransAccount' in [ unicode(o) for o in self['objectClass']]: self.attribs = self.attribs + crans_account_attribs self.full = True def compte(self, login = None, uidNumber=0, hash_pass = '', shell=config.login_shell): u"""Renvoie le nom du compte crans. S'il n'existe pas, et que uid est précisé, le crée.""" if u'posixAccount' in [str(o) for o in self.attrs['objectClass']]: return self.attrs['uid'][0] elif login: fn = crans_utils.strip_accents(unicode(self.attrs['prenom'][0]).capitalize()) ln = crans_utils.strip_accents(unicode(self.attrs['nom'][0]).capitalize()) login = crans_utils.strip_accents(login).lower() if not re.match('^[a-z][-a-z]{1,15}$', login): raise ValueError("Le login a entre 2 et 16 lettres, il peut contenir (pas au début) des - ") if crans_utils.mailexist(login): raise ValueError("Login existant ou correspondant à un alias mail.") home = u'/home/' + login if os.path.exists(home): raise ValueError('Création du compte impossible : home existant') if os.path.exists("/var/mail/" + login): raise ValueError('Création du compte impossible : /var/mail/%s existant' % login) if not self.full: self.attribs = self.attribs + crans_account_attribs self.full = True self['homeDirectory'] = [home] self['mail'] = [login + u"@crans.org"] self['uid' ] = [login] calias = crans_utils.strip_spaces(fn) + u'.' + crans_utils.strip_spaces(ln) if crans_utils.mailexist(calias): calias = login self['canonicalAlias'] = [calias] self['objectClass'] = [u'adherent', u'cransAccount', u'posixAccount', u'shadowAccount'] self['cn'] = [ fn + u' ' + ln ] self['loginShell'] = [unicode(shell)] self['userPassword'] = [unicode(hash_pass)] if uidNumber: if self.conn.search('(uidNumber=%s)' % uidNumber): raise ValueError(u'uidNumber pris') else: pool_uid = range(1001, 9999) random.shuffle(pool_uid) while len(pool_uid) > 0: uidNumber = pool_uid.pop() # On choisit un uid if not self.conn.search('(uidNumber=%s)' % uidNumber): break if not len(pool_uid): raise ValueError("Plus d'uid disponibles !") self['uidNumber'] = [unicode(uidNumber)] self['gidNumber'] = [unicode(config.gid)] self['gecos'] = [self._modifs['cn'][0] + u',,,'] #self.save() else: raise EnvironmentError("L'adhérent n'a pas de compte crans") @crans_object class club(proprio): u"""Club crans""" attribs = proprio.attribs + [attributs.cid, attributs.responsable, attributs.imprimeurClub] ldap_name = "club" @crans_object class machineFixe(machine): u"""Machine fixe""" ldap_name = "machineFixe" @crans_object class machineWifi(machine): u"""Machine wifi""" attribs = machine.attribs + [attributs.ipsec] ldap_name = "machineWifi" # À passer là où il faut # def set_ipv4(self, login=None): # u"""Définie une ipv4 à la machine si elle n'est possède pas déjà une.""" # if login is None: # login = self.conn.current_login # if not 'ipHostNumber' in self.attrs.keys() or not self['ipHostNumber']: # rid = self['rid']=[ unicode(self.conn._find_id('rid', range(config.rid['wifi'][0], config.rid['wifi'][1]+1))) ] # ip = self['ipHostNumber'] = [ unicode(crans_utils.ip4_of_rid(int(rid[0]))) ] # self.history_add(login, u"rid") # self.history_add(login, u"ipHostNumber (N/A -> %s)" % ip[0]) # self.save() # from gen_confs.dhcpd_new import dydhcp # dhcp=dydhcp() # dhcp.add_host(str(self['ipHostNumber'][0]), str(self['macAddress'][0]), str(self['host'][0])) @crans_object class machineCrans(machine): can_be_by = { variables.created: [attributs.nounou], variables.modified: [attributs.nounou], variables.deleted: [attributs.nounou], } attribs = machine.attribs + [attributs.prise, attributs.nombrePrises] ldap_name = "machineCrans" @crans_object class borneWifi(machine): can_be_by = { variables.created: [attributs.nounou], variables.modified: [attributs.nounou], variables.deleted: [attributs.nounou], } attribs = machine.attribs + [attributs.canal, attributs.puissance, attributs.hotspot, attributs.prise, attributs.positionBorne, attributs.nvram] ldap_name = "borneWifi" @crans_object class switchCrans(machine): can_be_by = { variables.created: [attributs.nounou], variables.modified: [attributs.nounou], variables.deleted: [attributs.nounou], } attribs = machine.attribs + [attributs.nombrePrises] ldap_name = "switchCrans" @crans_object class facture(CransLdapObject): can_be_by = { variables.created: [attributs.nounou, attributs.bureau, attributs.cableur], variables.modified: [attributs.nounou, attributs.bureau, attributs.cableur], variables.deleted: [attributs.nounou, attributs.bureau, attributs.cableur], } attribs = [attributs.fid, attributs.modePaiement, attributs.recuPaiement] ldap_name = "facture" @crans_object class service(CransLdapObject): ldap_name = "service"