From f4318415e8bec211416c5a8382f737bcefea157a Mon Sep 17 00:00:00 2001 From: Daniel STAN Date: Thu, 23 Jul 2015 16:29:56 +0200 Subject: [PATCH 01/66] =?UTF-8?q?history=5Fgen:=20garde=20login=20dans=20l?= =?UTF-8?q?'appel=20r=C3=A9cursif?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/objets.py b/objets.py index 3494958..52e8472 100644 --- a/objets.py +++ b/objets.py @@ -312,7 +312,8 @@ class CransLdapObject(object): "Génère une ligne d'historique pour l'arribut attr ou une ligne par attributs pour l'objet courant" if attr is None: for attr in self.keys(): - self.history_gen(attr) + self.history_gen(attr, login=login) + return def partial_name(name, max_len=14, start=7, end=7): if len(name) > max_len: return "%s…%s" % (name[:start], name[-end:]) From 5b66ae009502eca7034e968060344739055c02bd Mon Sep 17 00:00:00 2001 From: Daniel STAN Date: Thu, 23 Jul 2015 17:25:11 +0200 Subject: [PATCH 02/66] /usr/scripts sans / final --- lc_ldap.py | 7 +++---- objets.py | 6 +++--- shortcuts.py | 2 +- test.py | 2 -- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/lc_ldap.py b/lc_ldap.py index 8d9f0a6..9f4f7b4 100644 --- a/lc_ldap.py +++ b/lc_ldap.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- # # LC_LDAP.PY-- LightWeight CransLdap @@ -53,9 +52,9 @@ import variables import copy import itertools -## import de /usr/scripts/ -if not "/usr/scripts/" in sys.path: - sys.path.append('/usr/scripts/') +## import de /usr/scripts +if "/usr/scripts" not in sys.path: + sys.path.append('/usr/scripts') import gestion.config as config import cranslib.deprecated diff --git a/objets.py b/objets.py index 52e8472..eea54ab 100644 --- a/objets.py +++ b/objets.py @@ -56,9 +56,9 @@ import ldap_locks import variables import printing -## import de /usr/scripts/ -if not "/usr/scripts/" in sys.path: - sys.path.append('/usr/scripts/') +## import de /usr/scripts +if "/usr/scripts" not in sys.path: + sys.path.append('/usr/scripts') import gestion.config as config import gestion.config.impression diff --git a/shortcuts.py b/shortcuts.py index 13a1a49..6671272 100644 --- a/shortcuts.py +++ b/shortcuts.py @@ -15,7 +15,7 @@ try: from gestion import secrets_new as secrets except ImportError: sys.stderr.write("lc_ldap shortcuts: shaa, cannot import secrets_new. " + - "try again with /usr/scripts/ in PYTHONPATH " + + "try again with /usr/scripts in PYTHONPATH " + "(argv: %s)\n" % " ".join(getattr(sys, 'argv', []))) sys.path.append("/usr/scripts") from gestion import secrets_new as secrets diff --git a/test.py b/test.py index 8cd5591..ba7953d 100755 --- a/test.py +++ b/test.py @@ -14,8 +14,6 @@ import lc_ldap import shortcuts import variables -## import dans /usr/scripts/ -sys.path.append("/usr/scripts/") from gestion.affich_tools import anim, OK, cprint, ERREUR mail_format = False From e46d1fe338c6329825196116ba9105b36755466c Mon Sep 17 00:00:00 2001 From: Daniel STAN Date: Sat, 15 Aug 2015 01:00:33 +0200 Subject: [PATCH 03/66] ajoute fin_connexion/adhesion_*datetime* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parce que c'est plus facile à manipuler dans les templates. --- objets.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/objets.py b/objets.py index eea54ab..9d01651 100644 --- a/objets.py +++ b/objets.py @@ -1012,6 +1012,12 @@ class proprio(CransLdapObject): """Retourne la date de fin d'adhésion""" return max([float(facture.get('finAdhesion', [crans_utils.from_generalized_time_format(attributs.finAdhesion.default)])[0]) for facture in self.factures(refresh=True, mode="ro") if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', [''])[0] != ''] + [0.0]) + def fin_connexion_datetime(self): + return datetime.datetime.fromtimestamp(self.fin_connexion()) + + def fin_adhesion_datetime(self): + return datetime.datetime.fromtimestamp(self.fin_adhesion()) + def fin_connexion(self): """Retourne la date de fin de connexion""" return max([float(facture.get('finConnexion', [crans_utils.from_generalized_time_format(attributs.finConnexion.default)])[0]) for facture in self.factures(refresh=True, mode="ro") if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', [''])[0] != ''] + [0.0]) From f988e962c973aaa45da8136ed3cc61a07aad9a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Tue, 18 Aug 2015 04:16:02 +0200 Subject: [PATCH 04/66] =?UTF-8?q?Ajout=20de=20m=C3=A9thodes=20=5F=5Funicod?= =?UTF-8?q?e=5F=5F=20sur=20les=20objets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/objets.py b/objets.py index 9d01651..d05fc62 100644 --- a/objets.py +++ b/objets.py @@ -848,8 +848,11 @@ def crans_object(classe): class InetOrgPerson(CransLdapObject): __slots__ = () ldap_name = "inetOrgPerson" + def __unicode__(self): + return u"%s : cn=%s" % (self.__class__.__name__, self['cn'][0]) + def __repr__(self): - return str(self.__class__.__name__) + " : cn=" + str(self['cn'][0]) + return str(self.__unicode__()) pass class proprio(CransLdapObject): @@ -889,8 +892,11 @@ class proprio(CransLdapObject): """Renvoie la liste des clubs dont l'adherent est imprimeur (surchargée dans les objets adherent)""" return [] + def __unicode__(self): + return u"%s : nom=%s" % (self.__class__.__name__, self['nom'][0]) + def __repr__(self): - return str(self.__class__.__name__) + " : nom=" + str(self['nom'][0]) + return str(self.__unicode__()) def __init__(self, *args, **kwargs): super(proprio, self).__init__(*args, **kwargs) @@ -1166,8 +1172,11 @@ class machine(CransLdapObject): attributs.ipHostNumber, attributs.ip6HostNumber, attributs.historique, attributs.dnsIpv6, attributs.machineAlias] + def __unicode__(self): + return u"%s : host=%s" % (self.__class__.__name__, self['host'][0]) + def __repr__(self): - return str(self.__class__.__name__) + " : host=" + str(self['host'][0]) + return str(self.__unicode__()) def __init__(self, *args, **kwargs): super(machine, self).__init__(*args, **kwargs) @@ -1344,14 +1353,21 @@ class AssociationCrans(proprio): def delete(self, comm, login): pass + + def __unicode__(self): + return u"Le Crans" + def __repr__(self): - return str(self.__class__.__name__) + " : Le Crans" + return str(self.__unicode__()) class BaseInvites(proprio): u"""Un artefact de la base ldap""" __slots__ = () + def __unicode__(self): + return u"%s" % (self.__class__.__name__,) + def __repr__(self): - return str(self.__class__.__name__) + return str(self.__unicode__()) def delete(self, comm, login): raise EnvironmentError("Les pauvres invites") @@ -1371,8 +1387,11 @@ class adherent(proprio): ] ldap_name = "adherent" + def __unicode__(self): + return u"Adhérent : %s %s" % (self['prenom'][0], self['nom'][0]) + def __repr__(self): - return "Adhérent : " + str(self['prenom'][0]) + " " + str(self['nom'][0]) + return str(self.__unicode__()) def __init__(self, *args, **kwargs): super(adherent, self).__init__(*args, **kwargs) @@ -1415,8 +1434,11 @@ class club(proprio): def __init__(self, *args, **kwargs): super(club, self).__init__(*args, **kwargs) + def __unicode__(self): + return u"Club : %s" % (self['nom'][0],) + def __repr__(self): - return "Club : " + str(self['nom'][0]) + return str(self.__unicode__()) @crans_object class machineFixe(machine): @@ -1508,8 +1530,11 @@ class facture(CransLdapObject): attributs.finConnexion, attributs.controle ] ldap_name = "facture" + def __unicode__(self): + return u"Facture : fid=%s" % (self['fid'][0],) + def __repr__(self): - return str(self.__class__.__name__) + " : fid=" + str(self['fid'][0]) + return str(self.__unicode__()) def __init__(self, *args, **kwargs): self._proprio = None From 6c1dc6c5879f106f2af7ee8bd753706329d1e076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 23 Aug 2015 14:01:56 +0200 Subject: [PATCH 05/66] =?UTF-8?q?repr=20ne=20retourne=20rien=20d'encod?= =?UTF-8?q?=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/objets.py b/objets.py index d05fc62..d86247b 100644 --- a/objets.py +++ b/objets.py @@ -852,7 +852,7 @@ class InetOrgPerson(CransLdapObject): return u"%s : cn=%s" % (self.__class__.__name__, self['cn'][0]) def __repr__(self): - return str(self.__unicode__()) + return repr(self.__unicode__()) pass class proprio(CransLdapObject): @@ -896,7 +896,7 @@ class proprio(CransLdapObject): return u"%s : nom=%s" % (self.__class__.__name__, self['nom'][0]) def __repr__(self): - return str(self.__unicode__()) + return repr(self.__unicode__()) def __init__(self, *args, **kwargs): super(proprio, self).__init__(*args, **kwargs) @@ -1176,7 +1176,7 @@ class machine(CransLdapObject): return u"%s : host=%s" % (self.__class__.__name__, self['host'][0]) def __repr__(self): - return str(self.__unicode__()) + return repr(self.__unicode__()) def __init__(self, *args, **kwargs): super(machine, self).__init__(*args, **kwargs) @@ -1358,7 +1358,7 @@ class AssociationCrans(proprio): return u"Le Crans" def __repr__(self): - return str(self.__unicode__()) + return repr(self.__unicode__()) class BaseInvites(proprio): u"""Un artefact de la base ldap""" @@ -1367,7 +1367,7 @@ class BaseInvites(proprio): return u"%s" % (self.__class__.__name__,) def __repr__(self): - return str(self.__unicode__()) + return repr(self.__unicode__()) def delete(self, comm, login): raise EnvironmentError("Les pauvres invites") @@ -1391,7 +1391,7 @@ class adherent(proprio): return u"Adhérent : %s %s" % (self['prenom'][0], self['nom'][0]) def __repr__(self): - return str(self.__unicode__()) + return repr(self.__unicode__()) def __init__(self, *args, **kwargs): super(adherent, self).__init__(*args, **kwargs) @@ -1438,7 +1438,7 @@ class club(proprio): return u"Club : %s" % (self['nom'][0],) def __repr__(self): - return str(self.__unicode__()) + return repr(self.__unicode__()) @crans_object class machineFixe(machine): @@ -1534,7 +1534,7 @@ class facture(CransLdapObject): return u"Facture : fid=%s" % (self['fid'][0],) def __repr__(self): - return str(self.__unicode__()) + return repr(self.__unicode__()) def __init__(self, *args, **kwargs): self._proprio = None From 8f871ac1665440f67f8985469655920b6ac102ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 23 Aug 2015 14:28:39 +0200 Subject: [PATCH 06/66] Passe to_ldif en encodage de la base LDAP. --- attributs.py | 2 +- objets.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/attributs.py b/attributs.py index 0f3a690..2b1390f 100644 --- a/attributs.py +++ b/attributs.py @@ -242,7 +242,7 @@ class AttrsDict(dict): """ ldif = {} for attr, vals in self.items(): - ldif[attr] = [ str(val) for val in vals ] + ldif[attr] = [unicode(val).encode(config.ldap_encoding) for val in vals] return ldif class Attr(object): diff --git a/objets.py b/objets.py index d86247b..0088a0f 100644 --- a/objets.py +++ b/objets.py @@ -593,8 +593,8 @@ class CransLdapObject(object): ldif = self._modifs.to_ldif() orig_ldif = self.attrs.to_ldif() for attr in binary: - ldif['%s;binary' % attr]=ldif[attr] - orig_ldif['%s;binary' % attr]=orig_ldif.get(attr, []) + ldif['%s;binary' % (attr,)] = ldif[attr] + orig_ldif['%s;binary' % (attr,)] = orig_ldif.get(attr, []) del(ldif[attr]) try: del(orig_ldif[attr]) From 6142e6ef34b79b8b2bed1175c8adacc2cf3b27a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Mon, 24 Aug 2015 21:03:06 +0200 Subject: [PATCH 07/66] =?UTF-8?q?PEP8=20pour=20la=20m=C3=A9thode=20total?= =?UTF-8?q?=20de=20facture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/objets.py b/objets.py index 0088a0f..0053b06 100644 --- a/objets.py +++ b/objets.py @@ -1547,9 +1547,9 @@ class facture(CransLdapObject): return super(facture, self).__setitem__(attr, value) def total(self): - total=0 + total = 0 for article in self["article"]: - total+=int(article['nombre'])*float(article['pu']) + total += int(article['nombre'])*float(article['pu']) return total def crediter(self): From ad8d9026f067fb34ef6e76322de2d72bbd1e9f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Tue, 25 Aug 2015 02:38:12 +0200 Subject: [PATCH 08/66] "Mettez des capot^Wespaces" --- objets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/objets.py b/objets.py index 0053b06..f932ee6 100644 --- a/objets.py +++ b/objets.py @@ -1575,7 +1575,7 @@ class facture(CransLdapObject): with self.proprio() as proprio: proprio_save = credite_arts(proprio) # On vient de créditer, le paiement a été reçu - self['recuPaiement']=unicode(time.strftime("%Y-%m-%d %H:%M:%S")) + self['recuPaiement'] = unicode(time.strftime("%Y-%m-%d %H:%M:%S")) self._recuPaiement = True # Il faudrait faire quelquechose pour que si l'enregistrement suivant de la facture crash, From 12ba81e272842212e012336c98c38135b8f9aae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Wed, 26 Aug 2015 17:20:44 +0200 Subject: [PATCH 09/66] =?UTF-8?q?object.display=20est=20agnostique=20de=20?= =?UTF-8?q?ses=20arguments,=20qu'il=20forwarde=20=C3=A0=20sprintf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/objets.py b/objets.py index f932ee6..94c103e 100644 --- a/objets.py +++ b/objets.py @@ -295,8 +295,8 @@ class CransLdapObject(object): else: return self.attrs.items() - def display(self, historique=5, blacklist=5): - print printing.sprint(self, historique=historique, blacklist=blacklist) + def display(self, *args, **kwargs): + print printing.sprint(self, *args, **kwargs) def history_add(self, login, chain): """Ajoute une ligne à l'historique de l'objet. From 8ae5870468890354cb9318d5d44f9e45e2e5531d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Wed, 26 Aug 2015 17:22:51 +0200 Subject: [PATCH 10/66] =?UTF-8?q?Prise=20en=20charge=20des=20sshfp,=20et?= =?UTF-8?q?=20switches=20pour=20adresse/t=C3=A9l=C3=A9phone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- printing/templates/adherent | 4 ++++ printing/templates/machine | 3 +++ printing/templates/templates.py | 5 ++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/printing/templates/adherent b/printing/templates/adherent index 74c7e36..139bc59 100644 --- a/printing/templates/adherent +++ b/printing/templates/adherent @@ -3,13 +3,17 @@ {{["aid=",o.aid.0]|join|coul('bleu')}} {{"Nom : "|coul('gras')}}{{o.prenom|join(' ')}} {{o.nom|join(' ')}} {% endblock%} {% block proprio %} +{% if disp_telephone %} {{"Numéro de téléphone : "|coul('gras')}}{{o.tel|telephone|join(', ')}} +{% endif %} +{% if disp_adresse %} {% if o.chbre.0 == 'EXT' and o.postalAddress %} {{"Adresse : "|coul('gras')}}{{o.postalAddress.0}} {{o.postalAddress.1}} {{o.postalAddress.2}} {{o.postalAddress.3}} {% else %} {{"Chambre : "|coul('gras')}}{{o.chbre.0}} ({{o.chbre.0|string|prise_etat}}) {% endif %} +{% endif %} {{"Études : "|coul('gras')}}{{o.etudes|join(' ')}} {{adh}} {% if o.get('controle', []) and 'p' in o.controle.0.value %}{{"(OK)"|coul('vert')}}{% endif %} diff --git a/printing/templates/machine b/printing/templates/machine index 4b99f77..572984b 100644 --- a/printing/templates/machine +++ b/printing/templates/machine @@ -6,6 +6,9 @@ {{"IPv4 : "|coul('gras')}}{{o.ipHostNumber|join(', ')}} {% if o.ip6HostNumber %}{{"IPv6 : "|coul('gras')}}{{o.ip6HostNumber|join(', ')}} {% endif %} +{% if sshfp and o.sshFingerprint %} +{{"Fingerprints SSH : "|coul('gras')}}{{o.sshFingerprint|join('\n ')}} +{% endif %} {{"DnsIpv6 : "|coul('gras')}}{% if not o.dnsIpv6 or o.dnsIpv6.0.value %} {{"TRUE"|coul('vert')}} {% else %} diff --git a/printing/templates/templates.py b/printing/templates/templates.py index 404a381..f71e6bb 100644 --- a/printing/templates/templates.py +++ b/printing/templates/templates.py @@ -298,7 +298,10 @@ def blacklist(blacklist, params): def sprint(object, historique=5, blacklist_len=5, **params): from lc_ldap import objets, attributs - params.update({'historique':historique, "blacklist":blacklist_len}) + params.update({ + 'historique': historique, + 'blacklist': blacklist_len, + }) if isinstance(object, objets.machine): return machine(object, params) elif isinstance(object, objets.adherent): From 1353f00e173deb03cb5192adb2737e932b3c8a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Wed, 26 Aug 2015 18:43:41 +0200 Subject: [PATCH 11/66] Fait prendre un peu d'air au code --- objets.py | 366 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 246 insertions(+), 120 deletions(-) diff --git a/objets.py b/objets.py index 94c103e..136b5cc 100644 --- a/objets.py +++ b/objets.py @@ -83,7 +83,7 @@ def new_cransldapobject(conn, dn, mode='ro', uldif=None, lockId=None): else: res = conn.search_s(dn, 0) if not res: - raise ValueError ('objet inexistant: %s' % dn) + raise ValueError('objet inexistant: %s' % dn) _, attrs = res[0] classe = ObjectFactory.get(attrs['objectClass'][0]) @@ -100,9 +100,10 @@ class CransLdapObject(object): """ Qui peut faire quoi ? """ __slots__ = ("in_context", "conn", "lockId", "attrs", "_modifs", "dn", "parent_dn", "mode") - can_be_by = { variables.created: [attributs.nounou], - variables.modified: [attributs.nounou], - variables.deleted: [attributs.nounou], + can_be_by = { + variables.created: [attributs.nounou], + variables.modified: [attributs.nounou], + variables.deleted: [attributs.nounou], } attribs = [] @@ -169,7 +170,7 @@ class CransLdapObject(object): else: res = self.conn.search_s(dn, 0) if not res: - raise ValueError ('objet inexistant: %s' % dn) + 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 @@ -202,7 +203,7 @@ class CransLdapObject(object): for v in nldif[attr]: if v in vals: vals.remove(v) - nvals = [nldif[attr][vals.index(v)] for v in vals ] + 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 _id(self): @@ -259,11 +260,11 @@ class CransLdapObject(object): def c_mul(a, b): return eval(hex((long(a) * b) & 0xFFFFFFFFL)[:-1]) value = 0x345678 - l=0 + l = 0 keys = self.keys() keys.sort() for key in keys: - l+=len(self.attrs[key]) + l += len(self.attrs[key]) for item in self.attrs[key]: value = c_mul(1000003, value) ^ hash(item) value = value ^ l @@ -272,25 +273,25 @@ class CransLdapObject(object): return value def __iter__(self): - if self.mode in [ 'w', 'rw' ]: + if self.mode in ['w', 'rw']: return self._modifs.__iter__() else: return self.attrs.__iter__() def keys(self): - if self.mode in [ 'w', 'rw' ]: + if self.mode in ['w', 'rw']: return self._modifs.keys() else: return self.attrs.keys() def values(self): - if self.mode in [ 'w', 'rw' ]: + if self.mode in ['w', 'rw']: return self._modifs.values() else: return self.attrs.values() def items(self): - if self.mode in [ 'w', 'rw' ]: + if self.mode in ['w', 'rw']: return self._modifs.items() else: return self.attrs.items() @@ -381,7 +382,7 @@ class CransLdapObject(object): if not a in new_values: deleted.append(append(a)) if attr.historique == "info": - comm = u"%s%s%s" % ('+' if added else "", '-' if deleted else "", attr.ldap_name) + comm = u"%s%s%s" % ('+' if added else "", '-' if deleted else "", attr.ldap_name) elif attr.historique in ["full", "partial"]: comm = u"%s%s%s%s%s" % (attr.ldap_name, '+' if added else "", '+'.join(added), '-' if deleted else "", '-'.join(deleted)) @@ -438,7 +439,7 @@ class CransLdapObject(object): try: if self.conn.search(dn=self.dn): - raise ValueError ('objet existant: %s' % self.dn) + raise ValueError('objet existant: %s' % self.dn) except ldap.NO_SUCH_OBJECT: pass @@ -451,10 +452,10 @@ class CransLdapObject(object): self.history_add(login, u"Inscription") - ldif = self._modifs.to_ldif() + ldif = self._modifs.to_ldif() for attr in binary: - ldif['%s;binary' % attr]=ldif[attr] - del(ldif[attr]) + ldif['%s;binary' % attr] = ldif[attr] + del ldif[attr] # Création de la requête LDAP modlist = addModlist(ldif) # Requête LDAP de création de l'objet @@ -595,9 +596,9 @@ class CransLdapObject(object): for attr in binary: ldif['%s;binary' % (attr,)] = ldif[attr] orig_ldif['%s;binary' % (attr,)] = orig_ldif.get(attr, []) - del(ldif[attr]) + del ldif[attr] try: - del(orig_ldif[attr]) + del orig_ldif[attr] except KeyError: pass @@ -611,10 +612,10 @@ class CransLdapObject(object): return default def __getitem__(self, attr, default=None): - if self._modifs.has_key(attr) and self.mode in [ 'w', 'rw' ]: - return attributs.AttrsList(self, attr, [ v for v in self._modifs[attr] ]) + if self._modifs.has_key(attr) and self.mode in ['w', 'rw']: + return attributs.AttrsList(self, attr, [v for v in self._modifs[attr]]) elif self.attrs.has_key(attr): - return attributs.AttrsList(self, attr, [ v for v in self.attrs[attr] ]) + return attributs.AttrsList(self, attr, [v for v in self.attrs[attr]]) elif self.has_key(attr): return attributs.AttrsList(self, attr, []) if default is None else default else: @@ -700,8 +701,8 @@ class CransLdapObject(object): if not no_concurrent_lock and not attributs.AttributeFactory.get(attr).concurrent and self._modifs.get(attr, []) == self.attrs.get(attr, []) and attrs_before_verif != self.attrs.get(attr, []): if not self.in_context: cranslib.deprecated.usage("Des locks ne devrait être ajoutés que dans un context manager", level=2) - self.conn.lockholder.addlock("dn", "%s_%s" % (self.dn.replace('=', '-').replace(',','_'), attr), self.lockId) - locked.append(("dn", "%s_%s" % (self.dn.replace('=', '-').replace(',','_'), attr), self.lockId)) + self.conn.lockholder.addlock("dn", "%s_%s" % (self.dn.replace('=', '-').replace(',', '_'), attr), self.lockId) + locked.append(("dn", "%s_%s" % (self.dn.replace('=', '-').replace(',', '_'), attr), self.lockId)) try: # une fois le lock acquit, on vérifie que l'attribut n'a pas été édité entre temps if self.conn.search(dn=self.dn, scope=0)[0].get(attr, []) != self.attrs.get(attr, []): @@ -723,7 +724,7 @@ class CransLdapObject(object): self.conn.lockholder.removelock(attr, str(attribut), self.lockId) # Si on remet la valeur antérieure au lock, on le libère if not attributs.AttributeFactory.get(attr).concurrent and self._modifs.get(attr, []) != self.attrs.get(attr, []) and attrs_before_verif == self.attrs.get(attr, []): - self.conn.lockholder.removelock("dn", "%s_%s" % (self.dn.replace('=', '-').replace(',','_'), attr), self.lockId) + self.conn.lockholder.removelock("dn", "%s_%s" % (self.dn.replace('=', '-').replace(',', '_'), attr), self.lockId) # On met à jour self._modifs avec les nouvelles valeurs self._modifs[attr] = attrs_before_verif @@ -780,7 +781,7 @@ class CransLdapObject(object): Améliorations possibles: - Vérifier les blacklistes des machines pour les adhérents ? """ - blacklist_liste=[] + blacklist_liste = [] # blacklistes virtuelle si on est un adhérent pour carte étudiant et chambre invalides if isinstance(self, adherent): if self['chbre'][0] == '????': @@ -792,11 +793,11 @@ class CransLdapObject(object): blacklist_liste.append(bl) blacklist_liste.extend(bl for bl in self.get("blacklist", []) if bl.is_actif()) if excepts: - return [ b for b in blacklist_liste if b['type'] not in excepts ] + return [b for b in blacklist_liste if b['type'] not in excepts] else: return blacklist_liste - def blacklist(self, sanction, commentaire, debut="now", fin = '-'): + 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 @@ -858,24 +859,41 @@ class InetOrgPerson(CransLdapObject): class proprio(CransLdapObject): u""" Un propriétaire de machine (adhérent, club…) """ __slots__ = ("_machines", "_factures", "full") - 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,], + 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, + ], } - 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, attributs.rewriteMailHeaders, - attributs.mailExt, attributs.compteWiki, attributs.droits, - attributs.shadowExpire] - default_attribs = [attributs.nom, attributs.chbre, attributs.paiement, attributs.info, - attributs.blacklist, attributs.controle, attributs.historique, - attributs.debutAdhesion, attributs.finAdhesion, attributs.debutConnexion, - attributs.finConnexion] + 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, attributs.rewriteMailHeaders, + attributs.mailExt, attributs.compteWiki, attributs.droits, + attributs.shadowExpire, + ] + default_attribs = [ + attributs.nom, attributs.chbre, attributs.paiement, attributs.info, + attributs.blacklist, attributs.controle, attributs.historique, + attributs.debutAdhesion, attributs.finAdhesion, attributs.debutConnexion, + attributs.finConnexion, + ] @property def attribs(self): @@ -920,15 +938,15 @@ class proprio(CransLdapObject): self['uidNumber'] = [] self['gidNumber'] = [] self['gecos'] = [] - self['shadowExpire']=[] - self['derniereConnexion']=[] - self['mailExt']=[] - self['uid' ]=[] + self['shadowExpire'] = [] + self['derniereConnexion'] = [] + self['mailExt'] = [] + self['uid'] = [] self._modifs['objectClass'] = [u'adherent'] self.full = False - def compte(self, login = None, uidNumber=0, hash_pass = '', shell=config.login_shell): + 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 login est précisé, le crée.""" @@ -952,14 +970,14 @@ class proprio(CransLdapObject): raise ValueError('Création du compte impossible : /var/mail/%s existant' % str(login)) self._modifs['objectClass'] = [u'adherent', u'cransAccount', u'posixAccount', u'shadowAccount'] - self['uid' ] = [login] + self['uid'] = [login] self['homeDirectory'] = [home] self['mail'] = [login + u"@crans.org"] - calias = crans_utils.strip_spaces(fn) + u'.' + crans_utils.strip_spaces(ln) + '@crans.org' + calias = crans_utils.strip_spaces(fn) + u'.' + crans_utils.strip_spaces(ln) + '@crans.org' if crans_utils.mailexist(calias): calias = login self['canonicalAlias'] = [calias] - self['cn'] = [ fn + u' ' + ln ] + self['cn'] = [fn + u' ' + ln] self['loginShell'] = [unicode(shell)] self['userPassword'] = [unicode(hash_pass)] self["solde"] = 0.0 @@ -1016,7 +1034,11 @@ class proprio(CransLdapObject): def fin_adhesion(self): """Retourne la date de fin d'adhésion""" - return max([float(facture.get('finAdhesion', [crans_utils.from_generalized_time_format(attributs.finAdhesion.default)])[0]) for facture in self.factures(refresh=True, mode="ro") if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', [''])[0] != ''] + [0.0]) + return max([ + float(facture.get('finAdhesion', [crans_utils.from_generalized_time_format(attributs.finAdhesion.default)])[0]) + for facture in self.factures(refresh=True, mode="ro") + if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', [''])[0] != '' + ] + [0.0]) def fin_connexion_datetime(self): return datetime.datetime.fromtimestamp(self.fin_connexion()) @@ -1026,7 +1048,11 @@ class proprio(CransLdapObject): def fin_connexion(self): """Retourne la date de fin de connexion""" - return max([float(facture.get('finConnexion', [crans_utils.from_generalized_time_format(attributs.finConnexion.default)])[0]) for facture in self.factures(refresh=True, mode="ro") if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', [''])[0] != ''] + [0.0]) + return max([ + float(facture.get('finConnexion', [crans_utils.from_generalized_time_format(attributs.finConnexion.default)])[0]) + for facture in self.factures(refresh=True, mode="ro") + if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', [''])[0] != '' + ] + [0.0]) def paiement_ok(self, no_bl=False): u""" @@ -1069,7 +1095,7 @@ class proprio(CransLdapObject): 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" + 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 @@ -1082,7 +1108,7 @@ class proprio(CransLdapObject): """Renvoie la liste des machines""" if self._machines is None or refresh: try: - self._machines = self.conn.search(u'mid=*', dn = self.dn, scope = 1, mode=self.mode if mode is None else mode) + self._machines = self.conn.search(u'mid=*', dn=self.dn, scope=1, mode=self.mode if mode is None else mode) for m in self._machines: m._proprio = self except ldap.NO_SUCH_OBJECT: @@ -1098,7 +1124,7 @@ class proprio(CransLdapObject): refresh = True if self._factures is None or refresh: try: - self._factures = self.conn.search(u'fid=*', dn = self.dn, scope = 1, mode=mode) + self._factures = self.conn.search(u'fid=*', dn=self.dn, scope=1, mode=mode) for m in self._factures: m._proprio = self # Si on manipule un objet pas encore enregistré dans la la bdd @@ -1160,17 +1186,38 @@ class proprio(CransLdapObject): class machine(CransLdapObject): u""" Une machine """ __slots__ = ("_proprio", "_certificats") - can_be_by = { variables.created: [attributs.nounou, attributs.bureau, attributs.cableur, attributs.parent, attributs.respo], - variables.modified: [attributs.nounou, attributs.bureau, attributs.cableur, attributs.parent, attributs.respo], - variables.deleted: [attributs.nounou, attributs.bureau, attributs.cableur, attributs.parent, attributs.respo], + can_be_by = { + variables.created: [ + attributs.nounou, + attributs.bureau, + attributs.cableur, + attributs.parent, + attributs.respo, + ], + variables.modified: [ + attributs.nounou, + attributs.bureau, + attributs.cableur, + attributs.parent, + attributs.respo, + ], + variables.deleted: [ + attributs.nounou, + attributs.bureau, + attributs.cableur, + attributs.parent, + attributs.respo, + ], } - 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] + 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 __unicode__(self): return u"%s : host=%s" % (self.__class__.__name__, self['host'][0]) @@ -1191,7 +1238,7 @@ class machine(CransLdapObject): qu'un de ses certificats l'utilise. """ if attr in ['host', 'hostAlias']: - deleted = [ value for value in self[attr] if value not in values ] + deleted = [value for value in self[attr] if value not in values] for domain in deleted: for certificat in self.certificats(): if domain in certificat['hostCert']: @@ -1207,7 +1254,7 @@ class machine(CransLdapObject): """Renvoie la liste des certificats de la machine""" if refresh or self._certificats is None: try: - self._certificats = self.conn.search(u'xid=*', dn = self.dn, scope = 1, mode=self.mode) + self._certificats = self.conn.search(u'xid=*', dn=self.dn, scope=1, mode=self.mode) for m in self._certificats: m._machine = self except ldap.NO_SUCH_OBJECT: @@ -1216,7 +1263,7 @@ class machine(CransLdapObject): def blacklist_actif(self, excepts=[]): u"""Renvoie la liste des blacklistes actives sur la machine et le proprio""" - black=self.proprio().blacklist_actif(excepts) + black = self.proprio().blacklist_actif(excepts) black.extend(super(machine, self).blacklist_actif(excepts)) return black @@ -1379,12 +1426,13 @@ class adherent(proprio): @property def attribs(self): - return super(adherent, self).attribs + [attributs.aid, attributs.prenom, attributs.tel, - attributs.mail, attributs.mailInvalide, attributs.charteMA, - attributs.derniereConnexion, attributs.gpgFingerprint, - attributs.carteEtudiant, attributs.etudes, - attributs.postalAddress, attributs.gpgMail, - ] + return super(adherent, self).attribs + [ + attributs.aid, attributs.prenom, attributs.tel, + attributs.mail, attributs.mailInvalide, attributs.charteMA, + attributs.derniereConnexion, attributs.gpgFingerprint, + attributs.carteEtudiant, attributs.etudes, + attributs.postalAddress, attributs.gpgMail, + ] ldap_name = "adherent" def __unicode__(self): @@ -1402,13 +1450,13 @@ class adherent(proprio): def clubs(self): """Renvoie la liste des clubs dont l'adherent est responsable""" if self._clubs is None: - self._clubs = self.conn.search(u'responsable=%s' % self['aid'][0], scope = 1, mode=self.mode) + self._clubs = self.conn.search(u'responsable=%s' % self['aid'][0], scope=1, mode=self.mode) return self._clubs def imprimeur_clubs(self): """Renvoie la liste des clubs dont l'adherent est imprimeur""" if self._imprimeur_clubs is None: - self._imprimeur_clubs = self.conn.search(u'imprimeurClub=%s' % self['aid'][0], scope = 1, mode=self.mode) + self._imprimeur_clubs = self.conn.search(u'imprimeurClub=%s' % self['aid'][0], scope=1, mode=self.mode) return self._imprimeur_clubs def delete(self, comm="", login=None): @@ -1421,9 +1469,23 @@ class adherent(proprio): class club(proprio): u"""Club crans""" __slots__ = () - can_be_by = { variables.created: [attributs.nounou, attributs.bureau, attributs.cableur], - variables.modified: [attributs.nounou, attributs.bureau, attributs.respo, attributs.cableur, attributs.soi], - variables.deleted: [attributs.nounou, attributs.bureau], + can_be_by = { + variables.created: [ + attributs.nounou, + attributs.bureau, + attributs.cableur, + ], + variables.modified: [ + attributs.nounou, + attributs.bureau, + attributs.respo, + attributs.cableur, + attributs.soi, + ], + variables.deleted: [ + attributs.nounou, + attributs.bureau, + ], } ldap_name = "club" @@ -1488,46 +1550,88 @@ class machineWifi(machine): @crans_object class machineCrans(machine): __slots__ = () - can_be_by = { variables.created: [attributs.nounou], - variables.modified: [attributs.nounou], - variables.deleted: [attributs.nounou], + can_be_by = { + variables.created: [ + attributs.nounou, + ], + variables.modified: [ + attributs.nounou, + ], + variables.deleted: [ + attributs.nounou, + ], } - attribs = machine.attribs + [attributs.prise, attributs.nombrePrises] + attribs = machine.attribs + [ + attributs.prise, attributs.nombrePrises, + ] ldap_name = "machineCrans" @crans_object class borneWifi(machine): __slots__ = () - can_be_by = { variables.created: [attributs.nounou], - variables.modified: [attributs.nounou], - variables.deleted: [attributs.nounou], + 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] + attribs = machine.attribs + [ + attributs.canal, attributs.puissance, attributs.hotspot, + attributs.prise, attributs.positionBorne, attributs.nvram, + ] ldap_name = "borneWifi" @crans_object class switchCrans(machine): __slots__ = () - can_be_by = { variables.created: [attributs.nounou], - variables.modified: [attributs.nounou], - variables.deleted: [attributs.nounou], + can_be_by = { + variables.created: [ + attributs.nounou, + ], + variables.modified: [ + attributs.nounou, + ], + variables.deleted: [ + attributs.nounou, + ], } - attribs = machine.attribs + [attributs.nombrePrises] + attribs = machine.attribs + [ + attributs.nombrePrises, + ] ldap_name = "switchCrans" @crans_object class facture(CransLdapObject): __slots__ = ("_proprio", "_recuPaiement") - 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], + 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, - attributs.historique, attributs.article, attributs.info, - attributs.debutAdhesion, attributs.finAdhesion, attributs.debutConnexion, - attributs.finConnexion, attributs.controle ] + attribs = [ + attributs.fid, attributs.modePaiement, attributs.recuPaiement, + attributs.historique, attributs.article, attributs.info, + attributs.debutAdhesion, attributs.finAdhesion, attributs.debutConnexion, + attributs.finConnexion, attributs.controle, + ] ldap_name = "facture" def __unicode__(self): @@ -1542,9 +1646,9 @@ class facture(CransLdapObject): self._recuPaiement = True if self['recuPaiement'] else False def __setitem__(self, attr, value): - if self._recuPaiement and attr in ['article', 'modePaiement', 'recuPaiement']: - raise EnvironmentError("Paiement déjà effectué pour cette facture, impossible de modifier son contenu") - return super(facture, self).__setitem__(attr, value) + if self._recuPaiement and attr in ['article', 'modePaiement', 'recuPaiement']: + raise EnvironmentError("Paiement déjà effectué pour cette facture, impossible de modifier son contenu") + return super(facture, self).__setitem__(attr, value) def total(self): total = 0 @@ -1581,7 +1685,7 @@ class facture(CransLdapObject): # Il faudrait faire quelquechose pour que si l'enregistrement suivant de la facture crash, # on défait ce qu'on fait sur le proprio plus proprement if proprio_save: - proprio.save() + proprio.save() # On force l'enregistrement de la facture après avoir crédité try: @@ -1604,19 +1708,41 @@ class facture(CransLdapObject): @crans_object class baseCert(CransLdapObject): __slots__ = ("_machine",) - can_be_by = { variables.created: [attributs.nounou, attributs.bureau, attributs.parent], - variables.modified: [attributs.nounou, attributs.bureau, attributs.parent], - variables.deleted: [attributs.nounou, attributs.bureau, attributs.parent], + can_be_by = { + variables.created: [ + attributs.nounou, + attributs.bureau, + attributs.parent, + ], + variables.modified: [ + attributs.nounou, + attributs.bureau, + attributs.parent, + ], + variables.deleted: [ + attributs.nounou, + attributs.bureau, + attributs.parent, + ], } - default_attribs = [ attributs.xid, attributs.certificat, attributs.hostCert, attributs.historique, - attributs.info, attributs.csr ] + default_attribs = [ + attributs.xid, attributs.certificat, attributs.hostCert, attributs.historique, + attributs.info, attributs.csr, + ] - 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 ] + tlsa_attribs = [ + attributs.certificatUsage, attributs.selector, attributs.matchingType, + attributs.portTCPin, attributs.portUDPin, + ] - private_attribs = [ attributs.privatekey, attributs.encrypted ] + x509_attribs = [ + attributs.issuerCN, attributs.start, attributs.end, + attributs.crlUrl, attributs.revocked, attributs.serialNumber, + ] + + private_attribs = [ + attributs.privatekey, attributs.encrypted, + ] @property def attribs(self): @@ -1651,9 +1777,9 @@ class baseCert(CransLdapObject): s'il est réèlement présent dans les données du certificat. """ if attr in ['hostCert']: - deleted = [ value for value in self[attr] if value not in values ] + deleted = [value for value in self[attr] if value not in values] for domain in deleted: - if domain in [self['certificat'][0]['subject']['CN']] + self['certificat'][0]['extensions'].get('subjectAltName',[]): + if domain in [self['certificat'][0]['subject']['CN']] + self['certificat'][0]['extensions'].get('subjectAltName', []): raise EnvironmentError("Vous ne pouvez pas retirer le domaine %s alors qu'il est déclaré dans le certificat" % domain) def private(self, privatekey, encrypted): @@ -1663,8 +1789,8 @@ class baseCert(CransLdapObject): return self._modifs['objectClass'].append(u"privateKey") #self.attribs.extend(self.private_attribs) - self['encrypted']=encrypted - self['privatekey']=privatekey + self['encrypted'] = encrypted + self['privatekey'] = privatekey def tlsa(self, certificatUsage, matchingType): if not self.mode in ['w', 'rw']: @@ -1673,9 +1799,9 @@ class baseCert(CransLdapObject): return self._modifs['objectClass'].append(u"TLSACert") #self.attribs.extend(self.tlsa_attribs) - self['certificatUsage']=certificatUsage - self['matchingType']=matchingType - self['selector']=0 + self['certificatUsage'] = certificatUsage + self['matchingType'] = matchingType + self['selector'] = 0 def x509(self, issuerCN, start, end, serialNumber, crlUrl=None): if not self.mode in ['w', 'rw']: From 4a3434847e1798f55e931da858fe702c5c17efad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Wed, 26 Aug 2015 19:06:46 +0200 Subject: [PATCH 12/66] =?UTF-8?q?En=20mode=20bref=20affiche=20deux=20colon?= =?UTF-8?q?nes,=20une=20pour=20adh=C3=A9sion=20et=20une=20pour=20connexion?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 15 +++++++++ printing/templates/templates.py | 54 +++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/objets.py b/objets.py index 136b5cc..499557d 100644 --- a/objets.py +++ b/objets.py @@ -1054,6 +1054,17 @@ class proprio(CransLdapObject): if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', [''])[0] != '' ] + [0.0]) + def adhesion_ok(self, no_bl=False): + """Renvoie si le propriétaire a une adhésion en cours.""" + if self.dn == variables.base_dn: + return True + + fin_paiement = self.fin_adhesion() + + paiement = time.time() < fin_paiement or (config.periode_transitoire and config.debut_periode_transitoire <= fin_paiement <= config.fin_periode_transitoire) + + return paiement + def paiement_ok(self, no_bl=False): u""" Renvoie si le propriétaire a payé pour l'année en cours, en prenant en compte les périodes de transition et les blacklistes. @@ -1061,15 +1072,19 @@ class proprio(CransLdapObject): """ if self.dn == variables.base_dn: return True + if not no_bl: for bl in self.blacklist_actif(): if bl['type'] == 'paiement': return False + if isinstance(self, adherent): fin_paiement = min(self.fin_adhesion(), self.fin_connexion()) else: fin_paiement = self.fin_adhesion() + paiement = time.time() < fin_paiement or (config.periode_transitoire and config.debut_periode_transitoire <= fin_paiement <= config.fin_periode_transitoire) + return paiement def carte_ok(self): diff --git a/printing/templates/templates.py b/printing/templates/templates.py index f71e6bb..7603da5 100644 --- a/printing/templates/templates.py +++ b/printing/templates/templates.py @@ -223,28 +223,42 @@ def list_factures(factures, width=None): width=width) def list_adherents(adherents, width=None): - return tableau([ - [a['aid'][0], - u' '.join(unicode(i) for i in a['prenom'] + a['nom']), - a['chbre'][0], style('o', 'vert') if a.paiement_ok() else style('n', 'rouge'), - u', '.join(unicode(m['host'][0]).split('.',1)[0] for m in a.machines()) - ] for a in adherents ], - titre = [u'aid', u'Prénom Nom', u'Chbre', u'P', u'Machines'], - largeur = [5, 35, 5, 1, '*'], - alignement = ['d', 'c', 'c', 'c', 'g'], - width=width) + return tableau( + [ + [ + a['aid'][0], + u' '.join(unicode(i) for i in a['prenom'] + a['nom']), + a['chbre'][0], + style('o', 'vert') if a.adhesion_ok() else style('n', 'rouge'), + style('o', 'vert') if a.paiement_ok() else style('n', 'rouge'), + u', '.join(unicode(m['host'][0]).split('.', 1)[0] for m in a.machines()) + ] + for a in adherents + ], + titre=[u'aid', u'Prénom Nom', u'Chbre', u'A', u'C', u'Machines'], + largeur=[5, 35, 5, 1, 1, '*'], + alignement=['d', 'c', 'c', 'c', 'c', 'g'], + width=width + ) def list_clubs(clubs, width=None): - return tableau([ - [a['cid'][0], - u' '.join(unicode(i) for i in a['nom']), - a['chbre'][0], style('o', 'vert') if a.paiement_ok() else style('n', 'rouge'), - u', '.join(unicode(m['host'][0]).split('.',1)[0] for m in a.machines()) - ] for a in clubs ], - titre = [u'cid', u'Nom', u'Chbre', u'P', u'Machines'], - largeur = [5, 35, 5, 1, '*'], - alignement = ['d', 'c', 'c', 'c', 'g'], - width=width) + return tableau( + [ + [ + a['cid'][0], + u' '.join(unicode(i) for i in a['nom']), + a['chbre'][0], + style('o', 'vert') if a.adhesion_ok() else style('n', 'rouge'), + style('o', 'vert') if a.paiement_ok() else style('n', 'rouge'), + u', '.join(unicode(m['host'][0]).split('.',1)[0] for m in a.machines()) + ] + for a in clubs + ], + titre = [u'cid', u'Nom', u'Chbre', u'A', u'C', u'Machines'], + largeur = [5, 35, 5, 1, 1, '*'], + alignement = ['d', 'c', 'c', 'c', 'c', 'g'], + width=width + ) def proprio(proprio, params): params['o']=proprio From 0fb2cf4416dca3e1d5275a9f7f54cb37e1e4aa1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Thu, 27 Aug 2015 01:44:55 +0200 Subject: [PATCH 13/66] Plus d'infos sur les doublons dans une base ldap --- objets.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/objets.py b/objets.py index 499557d..d483f28 100644 --- a/objets.py +++ b/objets.py @@ -680,8 +680,11 @@ class CransLdapObject(object): try: for attribut in attrs_before_verif: # Vérification que (attr, value) est localement unique + # Il vaut mieux le vérifier ici, car l'erreur que LDAP + # lève n'est pas très claire. (mais il est clair qu'il ne + # tolère pas les doublons dans un objet) if attrs_before_verif.count(attribut) > 1: - raise ValueError("%s en double\n(%s)" % (attribut.legend if attribut.legend else attr, attribut)) + raise ValueError("%r en double\n(%r)" % (attribut.legend if attribut.legend else attr, attribut)) # On lock les nouvelles valeurs globalement unique if not no_uniq_lock and attribut.unique and not attribut in self._modifs.get(attr, []) and not attribut in attribut.unique_exclue: From 55eb87dce94300d3dbce1846991932d88817e53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Thu, 27 Aug 2015 02:16:49 +0200 Subject: [PATCH 14/66] history_gen ne respectait pas l'unicode sandwich --- objets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/objets.py b/objets.py index d483f28..c518676 100644 --- a/objets.py +++ b/objets.py @@ -372,9 +372,9 @@ class CransLdapObject(object): added = [] deleted = [] if attr.historique == "partial": - append = lambda x: partial_name(str(x)) + append = lambda x: partial_name(unicode(x)) else: - append = lambda x: str(x) + append = lambda x: unicode(x) for a in new_values: if not a in old_values: added.append(append(a)) From 83ca2e79912d308ef7bdc6cfcec5de30aa51c4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Thu, 27 Aug 2015 12:27:40 +0200 Subject: [PATCH 15/66] =?UTF-8?q?Fait=20en=20sorte=20que=20l'unicode=20san?= =?UTF-8?q?dwich=20soit=20respect=C3=A9=20dans=20tout=20history=5Fgen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/objets.py b/objets.py index c518676..4099ec3 100644 --- a/objets.py +++ b/objets.py @@ -345,8 +345,8 @@ class CransLdapObject(object): if attr.historique == "full": comm = u"%s (%s -> %s)" % (attr.ldap_name, old_values[0], new_values[0]) elif attr.historique == "partial": - old = partial_name(str(old_values[0])) - new = partial_name(str(new_values[0])) + old = partial_name(unicode(old_values[0])) + new = partial_name(unicode(new_values[0])) comm = u"%s (%s -> %s)" % (attr.ldap_name, old, new) elif attr.historique == "info": comm = u"%s" % attr.ldap_name @@ -355,7 +355,7 @@ class CransLdapObject(object): if attr.historique == "info": comm = u"+%s" % attr.ldap_name elif attr.historique in ["full", "partial"]: - new = str(new_values[0]) + new = unicode(new_values[0]) if attr.historique == "partial": new = partial_name(new) comm = u"%s+%s" % (attr.ldap_name, new) @@ -364,7 +364,7 @@ class CransLdapObject(object): if attr.historique == "info": comm = u"-%s" % attr.ldap_name elif attr.historique in ["full", "partial"]: - old = str(old_values[0]) + old = unicode(old_values[0]) if attr.historique == "partial": old = partial_name(old) comm = u"%s-%s" % (attr.ldap_name, old) From e59a468d19ceaa11fd425df2baf54c670b6a8225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 30 Aug 2015 02:28:23 +0200 Subject: [PATCH 16/66] =?UTF-8?q?La=20liste=20des=20droits=20basiques=20de?= =?UTF-8?q?=20la=20connexion=20ldap=20est=20encod=C3=A9e=20en=20unicode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lc_ldap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lc_ldap.py b/lc_ldap.py index 9f4f7b4..ef6f4fa 100644 --- a/lc_ldap.py +++ b/lc_ldap.py @@ -111,7 +111,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): if dn: self.conn = self.bind_s(dn, cred) self.dn = dn - self.droits = self.search_s(dn, ldap.SCOPE_BASE, attrlist=['droits'])[0][1].get('droits', []) + self.droits = [droit.decode(config.ldap_encoding) for droit in self.search_s(dn, ldap.SCOPE_BASE, attrlist=['droits'])[0][1].get('droits', [])] if dn == variables.admin_dn: self.droits += [attributs.nounou] # Il faut peupler current_login, qui sera utilisé pour écrire dans l'historique qui fait des modifications From 54a8d9c4cbed3a0f2fdb493426454d52053a49ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 6 Sep 2015 19:30:16 +0200 Subject: [PATCH 17/66] =?UTF-8?q?On=20ne=20rafra=C3=AEchit=20les=20facture?= =?UTF-8?q?s=20au=20plus=20qu'une=20fois=20toutes=20les=205=20sec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/objets.py b/objets.py index 4099ec3..2d52637 100644 --- a/objets.py +++ b/objets.py @@ -66,6 +66,7 @@ import cranslib.deprecated #: Champs à ignorer dans l'historique HIST_IGNORE_FIELDS = ["modifiersName", "entryCSN", "modifyTimestamp", "historique"] +FACTURES_REFRESH_PERIOD = 5 def new_cransldapobject(conn, dn, mode='ro', uldif=None, lockId=None): """Crée un objet :py:class:`CransLdapObject` en utilisant la classe correspondant à @@ -923,6 +924,7 @@ class proprio(CransLdapObject): super(proprio, self).__init__(*args, **kwargs) self._machines = None self._factures = None + self._factures_last_update = 0 def delete_compte(self, mail): # Je pense qu'en pratique cette vérification ne sert à rien puisqu'on se fera jetter à la tentative de modification @@ -1140,11 +1142,12 @@ class proprio(CransLdapObject): if self._factures: if self._factures[0].mode != mode: refresh = True - if self._factures is None or refresh: + if self._factures is None or (refresh and time.time() - self._factures_last_update > FACTURES_REFRESH_PERIOD): try: self._factures = self.conn.search(u'fid=*', dn=self.dn, scope=1, mode=mode) for m in self._factures: m._proprio = self + self._factures_last_update = time.time() # Si on manipule un objet pas encore enregistré dans la la bdd except ldap.NO_SUCH_OBJECT: self._factures = [] From 08cda773b0f6d001efaa070f54ab82548428dd18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 6 Sep 2015 19:56:02 +0200 Subject: [PATCH 18/66] proprio.__slots__ accueille une nouvelle variable --- objets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/objets.py b/objets.py index 2d52637..6867dc9 100644 --- a/objets.py +++ b/objets.py @@ -862,7 +862,7 @@ class InetOrgPerson(CransLdapObject): class proprio(CransLdapObject): u""" Un propriétaire de machine (adhérent, club…) """ - __slots__ = ("_machines", "_factures", "full") + __slots__ = ("_machines", "_factures", "full", '_factures_last_update') can_be_by = { variables.created: [ attributs.nounou, From 4a0a514986ae4ca0917569daf736bd7f91a996a4 Mon Sep 17 00:00:00 2001 From: Valentin Samir Date: Mon, 7 Sep 2015 12:50:39 +0200 Subject: [PATCH 19/66] =?UTF-8?q?Remise=20=C3=A0=201=20appel=20ldap=20pour?= =?UTF-8?q?=20la=20fonction=20allMachinesAdherents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lc_ldap.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lc_ldap.py b/lc_ldap.py index ef6f4fa..f557b50 100644 --- a/lc_ldap.py +++ b/lc_ldap.py @@ -38,6 +38,7 @@ import os import sys import re +import time from contextlib import contextmanager import ldap @@ -246,6 +247,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): res = {} parent = {} machines = {} + factures = {} # (proxying de la base ldap) for dn, attrs in self.search_s(variables.base_dn, scope=2): # On crée les listes des machines et propriétaires @@ -255,14 +257,26 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): if not machines.has_key(parent_dn): machines[parent_dn] = [] machines[parent_dn].append(m) + if dn.startswith('fid='): # les factures + f = objets.new_cransldapobject(self, dn, mode, uldif=ldif_to_uldif(attrs)) + parent_dn = dn.split(',', 1)[1] + if not factures.has_key(parent_dn): + factures[parent_dn] = [] + factures[parent_dn].append(f) elif (dn.startswith('aid=') or dn.startswith('cid=') or dn == variables.base_dn) and not parent.has_key(dn): parent[dn] = objets.new_cransldapobject(self, dn, mode, uldif=ldif_to_uldif(attrs)) allmachines = [] - for dn, mlist in machines.iteritems(): # on associe propriétaires et machines + for dn in parent: # on associe propriétaires et factures, machines + mlist = machines.get(dn, []) + flist = factures.get(dn, []) parent[dn]._machines = mlist + parent[dn]._factures = flist + parent[dn]._factures_last_update = time.time() for m in mlist: m._proprio = parent[dn] allmachines.append(m) + for f in flist: + f._proprio = parent[dn] return allmachines, parent.values() # on renvoie la liste des machines et des adherents (dont club et crans) def allMachines(self, mode='ro'): From c35878562a5c3ec6f2c1211c52e0da4516c73e7c Mon Sep 17 00:00:00 2001 From: Valentin Samir Date: Mon, 7 Sep 2015 12:56:19 +0200 Subject: [PATCH 20/66] Rafraichissement des factures par fin_connexion et fin_adhesion au plus une fois toutes les 1 min --- objets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/objets.py b/objets.py index 6867dc9..9ea3acc 100644 --- a/objets.py +++ b/objets.py @@ -66,7 +66,7 @@ import cranslib.deprecated #: Champs à ignorer dans l'historique HIST_IGNORE_FIELDS = ["modifiersName", "entryCSN", "modifyTimestamp", "historique"] -FACTURES_REFRESH_PERIOD = 5 +FACTURES_REFRESH_PERIOD = 60 def new_cransldapobject(conn, dn, mode='ro', uldif=None, lockId=None): """Crée un objet :py:class:`CransLdapObject` en utilisant la classe correspondant à @@ -1041,7 +1041,7 @@ class proprio(CransLdapObject): """Retourne la date de fin d'adhésion""" return max([ float(facture.get('finAdhesion', [crans_utils.from_generalized_time_format(attributs.finAdhesion.default)])[0]) - for facture in self.factures(refresh=True, mode="ro") + for facture in self.factures(refresh=(time.time() - self._factures_last_update > FACTURES_REFRESH_PERIOD)) if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', [''])[0] != '' ] + [0.0]) @@ -1055,7 +1055,7 @@ class proprio(CransLdapObject): """Retourne la date de fin de connexion""" return max([ float(facture.get('finConnexion', [crans_utils.from_generalized_time_format(attributs.finConnexion.default)])[0]) - for facture in self.factures(refresh=True, mode="ro") + for facture in self.factures(refresh=(time.time() - self._factures_last_update > FACTURES_REFRESH_PERIOD)) if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', [''])[0] != '' ] + [0.0]) @@ -1142,7 +1142,7 @@ class proprio(CransLdapObject): if self._factures: if self._factures[0].mode != mode: refresh = True - if self._factures is None or (refresh and time.time() - self._factures_last_update > FACTURES_REFRESH_PERIOD): + if self._factures is None or refresh: try: self._factures = self.conn.search(u'fid=*', dn=self.dn, scope=1, mode=mode) for m in self._factures: From d20fa54614d0fc9be0e5aefc57c7a01f4e3d9ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Thu, 10 Sep 2015 12:53:55 +0200 Subject: [PATCH 21/66] =?UTF-8?q?check=5Funiqueness=20sur=20les=20attribut?= =?UTF-8?q?s=20de=20type=20mail=20corrig=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attributs.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/attributs.py b/attributs.py index 2b1390f..6070c7b 100644 --- a/attributs.py +++ b/attributs.py @@ -743,22 +743,27 @@ class mail(rightProtectedAttr): if str(self) in liste_exclue: return if attr in ["mailAlias", "canonicalAlias", 'mail']: - mail, end = str(self).split('@', 1) - if end.startswith('crans'): - try: - smtp = smtplib.SMTP(smtpserv) - smtp.putcmd("vrfy", mail) - res = smtp.getreply()[0] in [250, 252] - smtp.close() - except: - raise ValueError('Serveur de mail injoignable') - - if res: - raise ValueError("Le mail %s est déjà pris." % (str(self))) - else: + mail = str(self) + # On commence par vérifier auprès du serveur SMTP + # si l'adresse email est prise sur le réseau. Cela permet + # de tester aussi les adresses statiquement écrites dans + # aliases.db. + try: + smtp = smtplib.SMTP(smtpserv) + smtp.putcmd("vrfy", mail) + res = smtp.getreply()[0] in [250, 252] + smtp.close() + except: + raise ValueError('Serveur de mail injoignable') + # Si le SMTP n'est pas joignable, on joue à la roulette + # russe en demandant à LDAP, sans vérifier les aliases + # statiques… check = self.conn.search(u'mail=%s' % mail) if len(check) >= 1: - raise ValueError("Le mail %s est déjà pris." % (str(self))) + res = True + + if res: + raise ValueError("Le mail %s est déjà pris." % (str(self))) def parse_value(self, mail): if not re.match(u'^[-_.0-9A-Za-z]+@([A-Za-z0-9]{1}[A-Za-z0-9-_]+[.])+[a-z]{2,6}$', mail): From fa7387420a9a97b83c34b282117398989710df04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Thu, 10 Sep 2015 14:52:57 +0200 Subject: [PATCH 22/66] =?UTF-8?q?On=20passe=20le=20TLD=20pour=20la=20regex?= =?UTF-8?q?p=20mail=20=C3=A0=2020?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * .cancerresearch fait déjà 14 --- attributs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributs.py b/attributs.py index 6070c7b..7404e82 100644 --- a/attributs.py +++ b/attributs.py @@ -766,7 +766,7 @@ class mail(rightProtectedAttr): raise ValueError("Le mail %s est déjà pris." % (str(self))) def parse_value(self, mail): - if not re.match(u'^[-_.0-9A-Za-z]+@([A-Za-z0-9]{1}[A-Za-z0-9-_]+[.])+[a-z]{2,6}$', mail): + if not re.match(u'^[-_.0-9A-Za-z]+@([A-Za-z0-9]{1}[A-Za-z0-9-_]+[.])+[a-z]{2,20}$', mail): raise ValueError("%s invalide %r" % (self.legend, mail)) self.value = mail From c91c89f3dfe657577c9d0a2408af27987358d2e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Thu, 10 Sep 2015 14:58:53 +0200 Subject: [PATCH 23/66] =?UTF-8?q?Donc=20on=20peste=20contre=20les=20sites?= =?UTF-8?q?=20qui=20interdisent=20les=20+=20dans=20les=20emails=C2=A0=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attributs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributs.py b/attributs.py index 7404e82..beb7020 100644 --- a/attributs.py +++ b/attributs.py @@ -766,7 +766,7 @@ class mail(rightProtectedAttr): raise ValueError("Le mail %s est déjà pris." % (str(self))) def parse_value(self, mail): - if not re.match(u'^[-_.0-9A-Za-z]+@([A-Za-z0-9]{1}[A-Za-z0-9-_]+[.])+[a-z]{2,20}$', mail): + if not re.match(u'^[-+_.0-9A-Za-z]+@([A-Za-z0-9]{1}[A-Za-z0-9-_]+[.])+[a-z]{2,20}$', mail): raise ValueError("%s invalide %r" % (self.legend, mail)) self.value = mail From 930d09b5398ec40f9753bc655415a6066db7b4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 20 Sep 2015 04:26:44 +0200 Subject: [PATCH 24/66] =?UTF-8?q?Repasser=20en=20read-only=20apr=C3=A8s=20?= =?UTF-8?q?un=20passage=20en=20context=20n'est=20pas=20adapt=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/objets.py b/objets.py index 9ea3acc..17fcbbf 100644 --- a/objets.py +++ b/objets.py @@ -230,8 +230,6 @@ class CransLdapObject(object): #self.save = self._out_of_context #self.create = self._out_of_context #self.delete = self._out_of_context - # On retombe en read only - self.mode = 'ro' # On purge les lock de l'objet self.conn.lockholder.purge(self.lockId) From f23644497b47955c8f6857be03af81e15d8d118a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 20 Sep 2015 12:06:05 +0200 Subject: [PATCH 25/66] =?UTF-8?q?DB=5FOVERRIDE=20vaut=20d=C3=A9j=C3=A0=20l?= =?UTF-8?q?ocalhost.=20On=20veut=20vo=20en=20usecase=20standard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crans_utils.py | 1 - shortcuts.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crans_utils.py b/crans_utils.py index ac0e78d..ffc5658 100644 --- a/crans_utils.py +++ b/crans_utils.py @@ -245,7 +245,6 @@ def escape(chaine): dans une requête ldap.""" return ldap.filter.escape_filter_chars(chaine) - def hash_password(password, salt=None, longueur=4): if longueur < 4: raise ValueError("salt devrait faire au moins 4 octets") diff --git a/shortcuts.py b/shortcuts.py index 6671272..d9d9910 100644 --- a/shortcuts.py +++ b/shortcuts.py @@ -51,7 +51,7 @@ def lc_ldap_test(*args, **kwargs): except OSError: pass # On impose le serveur - kwargs["uri"] = 'ldap://%s' % (DB_TEST_OVERRIDE or 'localhost') + kwargs["uri"] = 'ldap://%s' % (DB_TEST_OVERRIDE or 'vo.adm.crans.org') kwargs.setdefault("dn", 'cn=admin,dc=crans,dc=org') # Le mot de passe de la base de test kwargs.setdefault("cred", variables.ldap_test_password) From 854f774f37aa4c21c2e71eccfd6e57e896832911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Tue, 22 Sep 2015 21:16:56 +0200 Subject: [PATCH 26/66] Permet l'utilisation de datetimes avec generalizedTimeFormat --- attributs.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/attributs.py b/attributs.py index beb7020..d98a3dc 100644 --- a/attributs.py +++ b/attributs.py @@ -47,7 +47,8 @@ import random import string from unicodedata import normalize from crans_utils import format_tel, format_mac, mailexist, validate_name, ip4_of_rid, ip6_of_mac, fetch_cert_info -from crans_utils import to_generalized_time_format, from_generalized_time_format +from crans_utils import to_generalized_time_format, from_generalized_time_format, datetime_from_generalized_time_format +from crans_utils import datetime_to_generalized_time_format import itertools if '/usr/scripts' not in sys.path: @@ -655,6 +656,8 @@ class generalizedTimeFormat(Attr): elif isinstance(othertime, unicode) or isinstance(othertime, str): resource = generalizedTimeFormat(othertime, conn=None, Parent=None) return self._stamp == resource._stamp + elif isinstance(othertime, datetime.datetime): + return self._datetime == othertime else: return False @@ -671,6 +674,8 @@ class generalizedTimeFormat(Attr): elif isinstance(othertime, unicode) or isinstance(othertime, str): resource = generalizedTimeFormat(othertime, conn=None, Parent=None) return self._stamp < resource._stamp + elif isinstance(othertime, datetime.datetime): + return self._datetime < othertime else: return False @@ -688,12 +693,19 @@ class generalizedTimeFormat(Attr): if not ('Z' in gtf or '+' in gtf or '-' in gtf): self._stamp = gtf self.value = to_generalized_time_format(float(gtf)) + self._datetime = datetime_from_generalized_time_format(self.value) else: self._stamp = from_generalized_time_format(gtf) + self._datetime = datetime_from_generalized_time_format(gtf) self.value = gtf elif isinstance(gtf, float): self._stamp = gtf self.value = to_generalized_time_format(gtf) + self._datetime = datetime_from_generalized_time_format(self.value) + elif isinstance(gtf, datetime.datetime): + self._datetime = gtf + self.value = datetime_to_generalized_time_format(gtf) + self._stamp = from_generalized_time_format(self.value) @crans_attribute class debutAdhesion(generalizedTimeFormat): From 4858953064211e1b15f638050232d1dc65b1c71c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Tue, 22 Sep 2015 21:18:27 +0200 Subject: [PATCH 27/66] =?UTF-8?q?=5Fslots=20doit=20=C3=AAtre=20mis=20?= =?UTF-8?q?=C3=A0=20jour=20quand=20on=20ajoute=20des=20attributs=20=C3=A0?= =?UTF-8?q?=20un=20objet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attributs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributs.py b/attributs.py index d98a3dc..5687bce 100644 --- a/attributs.py +++ b/attributs.py @@ -637,7 +637,7 @@ class generalizedTimeFormat(Attr): une donnée de temps suivant la RFC 4517 """ - __slots__ = ("_stamp",) + __slots__ = ("_stamp", "_datetime",) default = "19700101000000Z" def __float__(self): From dae802b0575d9da10a076f822cf64a6910e9077c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Thu, 24 Sep 2015 23:23:44 +0200 Subject: [PATCH 28/66] =?UTF-8?q?Fonctionnalit=C3=A9=20pour=20g=C3=A9n?= =?UTF-8?q?=C3=A9rer=20un=20datetime=20timezone=20aware?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Prend un truc de la forme %Y%m%d%H%M%S, et une tz de la forme [+-]XXXX * On peut ne pas fournir le premier argument, auquel cas on prend la date actuele * On peut ne pas fournir la timezone, auquel cas elle sera fixée à celle du système si dateutil est installé, ou à UTC à défaut. * Si pas de pytz et que pas de tz fournie, dateutil est utilisé. * Si pas de pytz et pas de dateutil, le datetime ne sera pas timezone aware --- crans_utils.py | 52 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/crans_utils.py b/crans_utils.py index ffc5658..a7026b1 100644 --- a/crans_utils.py +++ b/crans_utils.py @@ -55,6 +55,11 @@ try: except: pytz = None +try: + import dateutil.tz +except: + dateutil = None + DEVNULL = open(os.devnull, 'w') def find_rid_plage(rid): @@ -340,21 +345,50 @@ def from_generalized_time_format(gtf): """ return time.mktime(time.strptime(gtf.split("-", 1)[0].split("+", 1)[0].split('Z', 1)[0], "%Y%m%d%H%M%S")) -def datetime_from_generalized_time_format(gtf): - """Returns a datetime from generalized time format +def localized_datetime(date=None, tz=None): + """Builds a datetime object based on a date string formatted as + %Y%m%d%H%M%S, and a tz timezone looking like +0200""" - """ + _notz = (tz is None) + + # Shit may happen + if tz == "Z": + tz = "+0000" + + if date is not None: + the_date = datetime.datetime.strptime(date, "%Y%m%d%H%M%S") + else: + the_date = datetime.datetime.now() + + # No timezone means we try to get from the system + # if we have dateutil, else, UTC. + if tz is None: + if dateutil is not None: + tz = datetime.datetime.now(dateutil.tz.tzlocal()).strftime("%z") + else: + tz = "+0000" + + # No pytz means no timezoned datetime + if pytz is not None: + the_timezone = pytz.FixedOffset(int(tz[0:-2])*60 + int(tz[-2:])) + the_date = the_timezone.localize(the_date) + the_date = the_timezone.normalize(the_date) + else: + # Maybe we can do something + if dateutil is not None: + if _notz: + the_date.replace(tzinfo=dateutil.tz.tzlocal()) + + return the_date + +def datetime_from_generalized_time_format(gtf): + """Returns a datetime from generalized time format""" if '-' in gtf or '+' in gtf: date, tz = gtf[0:14], gtf[14:] else: date = gtf.replace("Z", '') tz = '+0000' - the_date = datetime.datetime.strptime(date, "%Y%m%d%H%M%S") - if pytz is not None: - the_timezone = pytz.FixedOffset(int(tz[0:-2])*60 + int(tz[-2:])) - the_date = the_timezone.localize(the_date) - the_date = the_timezone.normalize(the_date) - return the_date + return localized_datetime(date, tz) def datetime_to_generalized_time_format(datetime_obj): """Transforms a datetime to a GTF""" From d1eb555599603e5630083b77e6ef1f0ce33b4861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sat, 26 Sep 2015 00:43:22 +0200 Subject: [PATCH 29/66] recuPaiement est un generalizedTimeFormat --- attributs.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/attributs.py b/attributs.py index 5687bce..ddb56ae 100644 --- a/attributs.py +++ b/attributs.py @@ -640,6 +640,9 @@ class generalizedTimeFormat(Attr): __slots__ = ("_stamp", "_datetime",) default = "19700101000000Z" + def __unicode__(self): + return self._datetime.strftime("%d/%m/%Y %H:%M:%S") + def __float__(self): return self._stamp @@ -1686,7 +1689,7 @@ class modePaiement(Attr): self.value = mode @crans_attribute -class recuPaiement(Attr): +class recuPaiement(generalizedTimeFormat): __slots__ = () ldap_name = "recuPaiement" can_modify = [cableur, nounou] From 9610cc6b0bf9b0f771a17b7c9b697b232399bbcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sat, 26 Sep 2015 00:54:42 +0200 Subject: [PATCH 30/66] Le nouveau type de recuPaiement n'est pas compatible avec != '' --- objets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/objets.py b/objets.py index 17fcbbf..175c730 100644 --- a/objets.py +++ b/objets.py @@ -1040,7 +1040,7 @@ class proprio(CransLdapObject): return max([ float(facture.get('finAdhesion', [crans_utils.from_generalized_time_format(attributs.finAdhesion.default)])[0]) for facture in self.factures(refresh=(time.time() - self._factures_last_update > FACTURES_REFRESH_PERIOD)) - if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', [''])[0] != '' + if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', []) ] + [0.0]) def fin_connexion_datetime(self): @@ -1054,7 +1054,7 @@ class proprio(CransLdapObject): return max([ float(facture.get('finConnexion', [crans_utils.from_generalized_time_format(attributs.finConnexion.default)])[0]) for facture in self.factures(refresh=(time.time() - self._factures_last_update > FACTURES_REFRESH_PERIOD)) - if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', [''])[0] != '' + if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', []) ] + [0.0]) def adhesion_ok(self, no_bl=False): @@ -1698,7 +1698,7 @@ class facture(CransLdapObject): with self.proprio() as proprio: proprio_save = credite_arts(proprio) # On vient de créditer, le paiement a été reçu - self['recuPaiement'] = unicode(time.strftime("%Y-%m-%d %H:%M:%S")) + self['recuPaiement'] = crans_utils.localized_datetime() self._recuPaiement = True # Il faudrait faire quelquechose pour que si l'enregistrement suivant de la facture crash, From c18de85fd2297b85d4cff239cf72e8120942dd8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sat, 26 Sep 2015 02:50:09 +0200 Subject: [PATCH 31/66] =?UTF-8?q?Enl=C3=A8ve=20=5F=5Funicode=5F=5F=20sur?= =?UTF-8?q?=20generalizedTimeFormat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attributs.py | 3 --- crans_utils.py | 6 ++++++ printing/templates/templates.py | 12 +++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/attributs.py b/attributs.py index ddb56ae..d3d901b 100644 --- a/attributs.py +++ b/attributs.py @@ -640,9 +640,6 @@ class generalizedTimeFormat(Attr): __slots__ = ("_stamp", "_datetime",) default = "19700101000000Z" - def __unicode__(self): - return self._datetime.strftime("%d/%m/%Y %H:%M:%S") - def __float__(self): return self._stamp diff --git a/crans_utils.py b/crans_utils.py index a7026b1..0e5357d 100644 --- a/crans_utils.py +++ b/crans_utils.py @@ -349,6 +349,12 @@ def localized_datetime(date=None, tz=None): """Builds a datetime object based on a date string formatted as %Y%m%d%H%M%S, and a tz timezone looking like +0200""" + if tz is None: + if "+" in date or '-' in date or 'Z' in date: + if date.endswith("Z"): + date = date[:-1] + "+0000" + date, tz = date[:-5], date[-5:] + _notz = (tz is None) # Shit may happen diff --git a/printing/templates/templates.py b/printing/templates/templates.py index 7603da5..4686f80 100644 --- a/printing/templates/templates.py +++ b/printing/templates/templates.py @@ -16,6 +16,7 @@ import gestion.annuaires_pg import gestion.hptools2 as hptools2 import lc_ldap.attributs as attributs +import lc_ldap.crans_utils as crans_utils def try_import(lib): """ @@ -201,16 +202,21 @@ def list_factures(factures, width=None): data = [] for facture in factures: controle = facture.get('controle', [""])[0] - if controle == "TRUE": + if controle == u"TRUE": controle = style(u"Validée", "vert") - elif controle == "FALSE": + elif controle == u"FALSE": controle = style(u"Invalide", "rouge") else: controle = u"N/A" + if facture.get('recuPaiement', []): + _dtime = crans_utils.datetime_from_generalized_time_format(unicode(facture.get('recuPaiement', [attributs.recuPaiement.default])[0])) + _recu = _dtime.strftime("%d/%m/%Y %H:%M:%S") + else: + _recu = False data.append([ facture['fid'][0], facture['modePaiement'][0], - style(facture.get('recuPaiement', [])[0], "vert") if facture.get('recuPaiement', []) else style("NON", "rouge"), + style(_recu, 'vert') if _recu else style(u"NON", 'rouge'), controle, ' '.join(attr['code'] for attr in facture.get('article',[])), u"%s €" % sum([float(a['pu'])*int(a['nombre']) for a in facture.get('article',[])]) From 8213b919eed683257d23bf70c8abd9dc33b7ccfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sat, 26 Sep 2015 03:01:59 +0200 Subject: [PATCH 32/66] Compatibilisation avec django --- attributs.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/attributs.py b/attributs.py index d3d901b..7c1e897 100644 --- a/attributs.py +++ b/attributs.py @@ -637,27 +637,27 @@ class generalizedTimeFormat(Attr): une donnée de temps suivant la RFC 4517 """ - __slots__ = ("_stamp", "_datetime",) + __slots__ = ("stamp", "datetime",) default = "19700101000000Z" def __float__(self): - return self._stamp + return self.stamp def __int__(self): - return int(self._stamp) + return int(self.stamp) def __eq__(self, othertime): if isinstance(othertime, generalizedTimeFormat): - return self._stamp == othertime._stamp + return self.stamp == othertime.stamp elif isinstance(othertime, float): - return self._stamp == othertime + return self.stamp == othertime elif isinstance(othertime, int): - return self._stamp == othertime + return self.stamp == othertime elif isinstance(othertime, unicode) or isinstance(othertime, str): resource = generalizedTimeFormat(othertime, conn=None, Parent=None) - return self._stamp == resource._stamp + return self.stamp == resource.stamp elif isinstance(othertime, datetime.datetime): - return self._datetime == othertime + return self.datetime == othertime else: return False @@ -666,16 +666,16 @@ class generalizedTimeFormat(Attr): def __lt__(self, othertime): if isinstance(othertime, generalizedTimeFormat): - return self._stamp < othertime._stamp + return self.stamp < othertime.stamp elif isinstance(othertime, float): - return self._stamp < othertime + return self.stamp < othertime elif isinstance(othertime, int): - return self._stamp < othertime + return self.stamp < othertime elif isinstance(othertime, unicode) or isinstance(othertime, str): resource = generalizedTimeFormat(othertime, conn=None, Parent=None) - return self._stamp < resource._stamp + return self.stamp < resource.stamp elif isinstance(othertime, datetime.datetime): - return self._datetime < othertime + return self.datetime < othertime else: return False @@ -691,21 +691,21 @@ class generalizedTimeFormat(Attr): def parse_value(self, gtf): if isinstance(gtf, str) or isinstance(gtf, unicode): if not ('Z' in gtf or '+' in gtf or '-' in gtf): - self._stamp = gtf + self.stamp = gtf self.value = to_generalized_time_format(float(gtf)) - self._datetime = datetime_from_generalized_time_format(self.value) + self.datetime = datetime_from_generalized_time_format(self.value) else: - self._stamp = from_generalized_time_format(gtf) - self._datetime = datetime_from_generalized_time_format(gtf) + self.stamp = from_generalized_time_format(gtf) + self.datetime = datetime_from_generalized_time_format(gtf) self.value = gtf elif isinstance(gtf, float): - self._stamp = gtf + self.stamp = gtf self.value = to_generalized_time_format(gtf) - self._datetime = datetime_from_generalized_time_format(self.value) + self.datetime = datetime_from_generalized_time_format(self.value) elif isinstance(gtf, datetime.datetime): - self._datetime = gtf + self.datetime = gtf self.value = datetime_to_generalized_time_format(gtf) - self._stamp = from_generalized_time_format(self.value) + self.stamp = from_generalized_time_format(self.value) @crans_attribute class debutAdhesion(generalizedTimeFormat): From af251f267b230d3bcf524bc8ef74a19263b1c70d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sat, 26 Sep 2015 21:21:56 +0200 Subject: [PATCH 33/66] La date peut valoir None dans localized_datetime --- crans_utils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crans_utils.py b/crans_utils.py index 0e5357d..3b358bc 100644 --- a/crans_utils.py +++ b/crans_utils.py @@ -350,10 +350,11 @@ def localized_datetime(date=None, tz=None): %Y%m%d%H%M%S, and a tz timezone looking like +0200""" if tz is None: - if "+" in date or '-' in date or 'Z' in date: - if date.endswith("Z"): - date = date[:-1] + "+0000" - date, tz = date[:-5], date[-5:] + if date is not None: + if "+" in date or '-' in date or 'Z' in date: + if date.endswith("Z"): + date = date[:-1] + "+0000" + date, tz = date[:-5], date[-5:] _notz = (tz is None) From 4de62cd0a87c37362d9d53e0cef58859f5d9efcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sat, 26 Sep 2015 21:48:43 +0200 Subject: [PATCH 34/66] Patch temporaire, le temps de transformer generalizedTimeFormat en datetime --- objets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/objets.py b/objets.py index 175c730..ebe3886 100644 --- a/objets.py +++ b/objets.py @@ -1698,7 +1698,7 @@ class facture(CransLdapObject): with self.proprio() as proprio: proprio_save = credite_arts(proprio) # On vient de créditer, le paiement a été reçu - self['recuPaiement'] = crans_utils.localized_datetime() + self['recuPaiement'] = crans_utils.datetime_to_generalized_time_format(crans_utils.localized_datetime()) self._recuPaiement = True # Il faudrait faire quelquechose pour que si l'enregistrement suivant de la facture crash, From a1f27f9f1a61653efb09099b58fd009c64e46ef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sat, 3 Oct 2015 14:09:08 +0200 Subject: [PATCH 35/66] Les timestamps c'est merdique, maintenant tout est du datetime --- attributs.py | 46 +++++++++++++------------------- objets.py | 47 ++++++++++++++++++++++++--------- printing/templates/templates.py | 14 +++++++--- 3 files changed, 64 insertions(+), 43 deletions(-) diff --git a/attributs.py b/attributs.py index 7c1e897..5358be2 100644 --- a/attributs.py +++ b/attributs.py @@ -638,26 +638,30 @@ class generalizedTimeFormat(Attr): """ __slots__ = ("stamp", "datetime",) - default = "19700101000000Z" + python_type = datetime.datetime + default = datetime_from_generalized_time_format("19700101000000Z") + + def __unicode__(self): + return unicode(datetime_to_generalized_time_format(self.value)) def __float__(self): - return self.stamp + return from_generalized_time_format(unicode(self)) def __int__(self): - return int(self.stamp) + return int(float(self)) def __eq__(self, othertime): if isinstance(othertime, generalizedTimeFormat): - return self.stamp == othertime.stamp + return self.value == othertime.value elif isinstance(othertime, float): - return self.stamp == othertime + return float(self) == othertime elif isinstance(othertime, int): - return self.stamp == othertime + return int(self) == othertime elif isinstance(othertime, unicode) or isinstance(othertime, str): resource = generalizedTimeFormat(othertime, conn=None, Parent=None) - return self.stamp == resource.stamp + return self == resource elif isinstance(othertime, datetime.datetime): - return self.datetime == othertime + return self.value == othertime else: return False @@ -666,16 +670,16 @@ class generalizedTimeFormat(Attr): def __lt__(self, othertime): if isinstance(othertime, generalizedTimeFormat): - return self.stamp < othertime.stamp + return self.value < othertime.value elif isinstance(othertime, float): - return self.stamp < othertime + return float(self) < othertime elif isinstance(othertime, int): - return self.stamp < othertime + return int(self) < othertime elif isinstance(othertime, unicode) or isinstance(othertime, str): resource = generalizedTimeFormat(othertime, conn=None, Parent=None) - return self.stamp < resource.stamp + return self < resource elif isinstance(othertime, datetime.datetime): - return self.datetime < othertime + return self.value < othertime else: return False @@ -690,21 +694,9 @@ class generalizedTimeFormat(Attr): def parse_value(self, gtf): if isinstance(gtf, str) or isinstance(gtf, unicode): - if not ('Z' in gtf or '+' in gtf or '-' in gtf): - self.stamp = gtf - self.value = to_generalized_time_format(float(gtf)) - self.datetime = datetime_from_generalized_time_format(self.value) - else: - self.stamp = from_generalized_time_format(gtf) - self.datetime = datetime_from_generalized_time_format(gtf) - self.value = gtf - elif isinstance(gtf, float): - self.stamp = gtf - self.value = to_generalized_time_format(gtf) - self.datetime = datetime_from_generalized_time_format(self.value) + self.value = datetime_from_generalized_time_format(gtf) elif isinstance(gtf, datetime.datetime): - self.datetime = gtf - self.value = datetime_to_generalized_time_format(gtf) + self.value = gtf self.stamp = from_generalized_time_format(self.value) @crans_attribute diff --git a/objets.py b/objets.py index ebe3886..be26b70 100644 --- a/objets.py +++ b/objets.py @@ -1038,33 +1038,42 @@ class proprio(CransLdapObject): def fin_adhesion(self): """Retourne la date de fin d'adhésion""" return max([ - float(facture.get('finAdhesion', [crans_utils.from_generalized_time_format(attributs.finAdhesion.default)])[0]) + facture.get('finAdhesion', [attributs.finAdhesion.default])[0] for facture in self.factures(refresh=(time.time() - self._factures_last_update > FACTURES_REFRESH_PERIOD)) if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', []) - ] + [0.0]) - - def fin_connexion_datetime(self): - return datetime.datetime.fromtimestamp(self.fin_connexion()) - - def fin_adhesion_datetime(self): - return datetime.datetime.fromtimestamp(self.fin_adhesion()) + ] + [attributs.finAdhesion.default]) def fin_connexion(self): """Retourne la date de fin de connexion""" return max([ - float(facture.get('finConnexion', [crans_utils.from_generalized_time_format(attributs.finConnexion.default)])[0]) + facture.get('finConnexion', [attributs.finConnexion.default])[0] for facture in self.factures(refresh=(time.time() - self._factures_last_update > FACTURES_REFRESH_PERIOD)) if facture.get('controle', [''])[0] != u"FALSE" and facture.get('recuPaiement', []) - ] + [0.0]) + ] + [attributs.finConnexion.default]) def adhesion_ok(self, no_bl=False): """Renvoie si le propriétaire a une adhésion en cours.""" + if self.dn == variables.base_dn: return True + _now = crans_utils.localized_datetime() + fin_paiement = self.fin_adhesion() - paiement = time.time() < fin_paiement or (config.periode_transitoire and config.debut_periode_transitoire <= fin_paiement <= config.fin_periode_transitoire) + paiement = ( + _now < fin_paiement + or + ( + config.periode_transitoire + and + ( + crans_utils.datetime_from_generalized_time_format(config.gtf_debut_periode_transitoire) + <= fin_paiement + <= crans_utils.datetime_from_generalized_time_format(config.gtf_fin_periode_transitoire) + ) + ) + ) return paiement @@ -1081,12 +1090,26 @@ class proprio(CransLdapObject): if bl['type'] == 'paiement': return False + _now = crans_utils.localized_datetime() + if isinstance(self, adherent): fin_paiement = min(self.fin_adhesion(), self.fin_connexion()) else: fin_paiement = self.fin_adhesion() - paiement = time.time() < fin_paiement or (config.periode_transitoire and config.debut_periode_transitoire <= fin_paiement <= config.fin_periode_transitoire) + paiement = ( + _now < fin_paiement + or + ( + config.periode_transitoire + and + ( + crans_utils.datetime_from_generalized_time_format(config.gtf_debut_periode_transitoire) + <= fin_paiement + <= crans_utils.datetime_from_generalized_time_format(config.gtf_fin_periode_transitoire) + ) + ) + ) return paiement diff --git a/printing/templates/templates.py b/printing/templates/templates.py index 4686f80..29d33cb 100644 --- a/printing/templates/templates.py +++ b/printing/templates/templates.py @@ -267,25 +267,31 @@ def list_clubs(clubs, width=None): ) def proprio(proprio, params): - params['o']=proprio - etat_administratif=[] + _now = crans_utils.localized_datetime() + params['o'] = proprio + etat_administratif = [] + if proprio.paiement_ok(): etat_administratif.append(style(u"à jour", "vert")) + if not proprio.paiement_ok(): etat_administratif.append(style(u"cotisation non réglée", "violet")) - if proprio.fin_adhesion() >= time.time(): + + if proprio.fin_adhesion() >= _now: adh = style(u"Adhésion jusqu'au %s" % (time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(proprio.fin_adhesion())),), "vert") elif proprio.paiement_ok(): adh = style(u"Adhésion terminée, mais il y a un sursis.", 'orange') else: adh = style(u"Pas adhérent actuellement.", 'rouge') + params["adh"] = adh - if proprio.fin_connexion() >= time.time(): + if proprio.fin_connexion() >= _now: conn = style(u"Connexion jusqu'au %s" % (time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(proprio.fin_connexion())),), "vert") elif proprio.paiement_ok(): conn = style(u"Connexion terminée, mais il y a un sursis.", 'orange') else: conn = style(u"Pas connecté actuellement.", 'rouge') + params["conn"] = conn params['etat_administratif'] = etat_administratif From 48de31425da54b441d74b02bfd8042ae53492960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Thu, 8 Oct 2015 15:09:04 +0200 Subject: [PATCH 36/66] Ajout de documentation sur is_modifiable dans userPassword --- attributs.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/attributs.py b/attributs.py index 5358be2..eaeea22 100644 --- a/attributs.py +++ b/attributs.py @@ -351,9 +351,7 @@ class Attr(object): raise UniquenessError("%s déjà existant" % attr, [r.dn for r in res]) def is_modifiable(self, liste_droits): - """ - L'attribut est-il modifiable par un des droits dans liste_droits ? - """ + """L'attribut est-il modifiable par un des droits dans liste_droits ?""" return not set(liste_droits).isdisjoint(self.can_modify) class AttributeFactory(object): @@ -394,13 +392,26 @@ class rightProtectedAttr(Attr): """ On permet la modification à un utilisateur """ + # Si on est soi-même, on peut changer son mot de passe sans condition if not soi in liste_droits: + modifiables = set() + + # On regarde la liste des droits qu'a la connexion courante, ce sont a priori + # les droits de l'utilisateur qui modifie le mot de passe, ajoutés de soi/parent + # les cas échéants. for i in liste_droits: + + # Si le droit est un droit "superviseur" (ie ayant le droit de modifier certains + # utilisateurs ayant un des droits "supervisés", on ajoute la liste des droits + # supervisés aux droits modifiables if i in DROITS_SUPERVISEUR: modifiables = modifiables.union(DROITS_SUPERVISEUR[i]) modifiables = list(modifiables) + # On prend les droits de l'utilisateur dont on cherche à modifier le mot de passe + # et on les compare à la liste des droits que l'utilisateur qui modifie a le droit + # de modifier. S'il y en a un qui n'est pas dans la liste des droits modifiables, on jette. for droit in self.parent.get('droits', []): if droit not in modifiables and droit in TOUS_DROITS: return False From bbea130179105fc116cbac2743df0ab1dfd83581 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 11 Oct 2015 16:29:28 +0200 Subject: [PATCH 37/66] =?UTF-8?q?Le=20cableur=20peut=20modifier=20l'ipsec,?= =?UTF-8?q?=20notemment=20=C3=A0=20la=20creation=20de=20la=20machine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attributs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributs.py b/attributs.py index eaeea22..d6e9d2a 100644 --- a/attributs.py +++ b/attributs.py @@ -1107,7 +1107,7 @@ class ipsec(Attr): legend = u'Clef wifi' category = 'wifi' ldap_name = "ipsec" - can_modify = [nounou, parent] + can_modify = [nounou, cableur, parent] historique = "info" default = u'auto' From 3e71d60304b448bc20573cc52b796f721d3fe109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 18 Oct 2015 01:17:58 +0200 Subject: [PATCH 38/66] Bloc de code mort --- ldap_locks.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/ldap_locks.py b/ldap_locks.py index 1492906..381e22c 100644 --- a/ldap_locks.py +++ b/ldap_locks.py @@ -214,6 +214,3 @@ class LdapLockHolder: except ldap.INVALID_DN_SYNTAX: print '%s=%s,%s' % (item, value, LOCKS_DN) raise - except ValueError as e: - self.removelock(item, value, Id, force=True) - raise LockNotFound() From 91fa2974e43d71a603e136fc36784aabb7f45b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 18 Oct 2015 01:18:13 +0200 Subject: [PATCH 39/66] =?UTF-8?q?Import=20inutilis=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crans_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/crans_utils.py b/crans_utils.py index 3b358bc..6ea5e20 100644 --- a/crans_utils.py +++ b/crans_utils.py @@ -47,7 +47,6 @@ if '/usr/scripts' not in sys.path: sys.path.append('/usr/scripts') from gestion import config from unicodedata import normalize -import subprocess from netifaces import interfaces, ifaddresses, AF_INET try: From d7800bbfb126d508d3bb96779e1d89a4037db5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 18 Oct 2015 01:21:43 +0200 Subject: [PATCH 40/66] Fix "too many values to unpack" --- ldap_locks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ldap_locks.py b/ldap_locks.py index 381e22c..9a4c164 100644 --- a/ldap_locks.py +++ b/ldap_locks.py @@ -179,7 +179,7 @@ class LdapLockHolder: host, pid, begin = self.getlock(item, value) time_left = self.timeout - (time.time() - begin) if time_left <= delai: - raise LockExpired("Le lock sur la donnée %r=%r à expiré" % (item, value, time_left)) + raise LockExpired("Le lock sur la donnée %r=%r à expiré" % (item, value)) def removelock(self, item, value, Id='default', force=False): """ From 9388e1d2175b02fd418a779ebf117e609014d76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 18 Oct 2015 20:05:41 +0200 Subject: [PATCH 41/66] CransLdapObject._id -> CransLdapObject.oid --- objets.py | 2 +- printing/templates/facture | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/objets.py b/objets.py index be26b70..6f8b422 100644 --- a/objets.py +++ b/objets.py @@ -207,7 +207,7 @@ class CransLdapObject(object): 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 _id(self): + def oid(self): """Retourne l'id de l'objet courant""" if isinstance(self, adherent): return self['aid'] diff --git a/printing/templates/facture b/printing/templates/facture index 7aa46db..cd9040c 100644 --- a/printing/templates/facture +++ b/printing/templates/facture @@ -1,4 +1,4 @@ -{{["fid=",o._id().0]|join|coul('bleu')}} {{"À : "|coul('gras')}}{{o.proprio().prenom|join(' ')}} {{o.proprio().nom|join(' ')}} ({{o.proprio()._id().0}}) +{{["fid=",o.oid().0]|join|coul('bleu')}} {{"À : "|coul('gras')}}{{o.proprio().prenom|join(' ')}} {{o.proprio().nom|join(' ')}} ({{o.proprio().oid().0}}) {% if o.article %} {{"Article : "|coul('gras')}} Prix N° Total Commentaire {% set total = [] %}{% for a in o.article %} From 2a806bb3670c7f67a964fe0e4dcd78d7a3084820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 18 Oct 2015 21:01:05 +0200 Subject: [PATCH 42/66] =?UTF-8?q?Splitte=20oid=20entre=20les=20diff=C3=A9r?= =?UTF-8?q?ents=20objets=20concern=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/objets.py b/objets.py index 6f8b422..db1cfe0 100644 --- a/objets.py +++ b/objets.py @@ -209,16 +209,7 @@ class CransLdapObject(object): def oid(self): """Retourne l'id de l'objet courant""" - if isinstance(self, adherent): - return self['aid'] - elif isinstance(self, machine): - return self['mid'] - elif isinstance(self, club): - return self['cid'] - elif isinstance(self, facture): - return self['fid'] - else: - return [self.dn] + return [self.dn.split(",")[0].split("=")[1]] def _out_of_context(self, *args, **kwargs): raise EnvironmentError("Hors du context, impossible de faire des écritures") @@ -1286,6 +1277,10 @@ class machine(CransLdapObject): if domain in certificat['hostCert']: raise EnvironmentError("Vous devez d'abord supprimer ou éditer les certificats utilisant le nom de domaine %s avant de le retirer de la machine" % domain) + def oid(self): + """Retourne l'id de l'objet courant""" + return self['mid'] + def proprio(self, mode=None, refresh=False): u"""Renvoie le propriétaire de la machine""" if not hasattr(self, '_proprio') or not self._proprio or refresh: @@ -1507,6 +1502,10 @@ class adherent(proprio): raise EnvironmentError("L'adhérent est responsable ou imprimeur pour les clubs %s, suppression impossible" % ", ".join(str(c["cid"][0]) for c in clubs)) super(adherent, self).delete(comm, login) + def oid(self): + """Retourne l'id de l'objet courant""" + return self['aid'] + @crans_object class club(proprio): u"""Club crans""" @@ -1544,6 +1543,10 @@ class club(proprio): def __repr__(self): return repr(self.__unicode__()) + def oid(self): + """Retourne l'id de l'objet courant""" + return self['cid'] + @crans_object class machineFixe(machine): u"""Machine fixe""" @@ -1692,6 +1695,10 @@ class facture(CransLdapObject): raise EnvironmentError("Paiement déjà effectué pour cette facture, impossible de modifier son contenu") return super(facture, self).__setitem__(attr, value) + def oid(self): + """Retourne l'id de l'objet courant""" + return self['fid'] + def total(self): total = 0 for article in self["article"]: From 5b57476ab46cdf0e38629637a74bac9ebef3047c Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Fri, 23 Oct 2015 19:24:44 +0200 Subject: [PATCH 43/66] Nouvelle fonction de facture qui rempli les attributs finadh, debutadh , debu con et fin con sur une facture adh ou connexion --- objets.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/objets.py b/objets.py index db1cfe0..fcc2190 100644 --- a/objets.py +++ b/objets.py @@ -1724,6 +1724,7 @@ class facture(CransLdapObject): proprio_save = True return proprio_save + if not self._recuPaiement: with self.proprio() as proprio: proprio_save = credite_arts(proprio) @@ -1748,6 +1749,38 @@ class facture(CransLdapObject): proprio.save() raise + def adhesion_connexion(self): + u"""Effectue l'adhésion ou la connexion, rempli les attribus ldap correspondants""" + + def do_adh_conn(proprio): + u"""Rempli les attribus qu'il faut sur les factures d'adhésion et de connexion""" + # On parse les art et on update les attribus + _now = crans_utils.localized_datetime() + for art in self['article']: + if 'ADH' in art['code']: + self['debutAdhesion'] = max(proprio.fin_adhesion(), _now) + self['finAdhesion'] = max(proprio.fin_adhesion(), _now).replace(year=max(proprio.fin_adhesion(), _now).year + 1) + if 'CAI' in art['code']: + nbr_mois = int(art['code'].replace('CAI','')) + self['debutConnexion'] = max(proprio.fin_connexion(), _now) + con_month = max(proprio.fin_connexion(), _now).month + con_year = max(proprio.fin_connexion(), _now).year + self['finConnexion'] = max(proprio.fin_connexion(), _now).replace(year=con_year + ((con_month + nbr_mois) // 12), month= (con_month + nbr_mois) % 12) + return + + # On effectue les opérations + do_adh_conn(self.proprio()) + self['recuPaiement'] = crans_utils.datetime_to_generalized_time_format(crans_utils.localized_datetime()) + self._recuPaiement = True + + # On force l'enregistrement de la facture après avoir crédité + if self.exists(): + self.save() + else: + self.create() + + + def proprio(self, refresh=False): u"""Renvoie le propriétaire de la facture""" if refresh or not self._proprio: From 8b9b9dc16954d6ecefdf72b3e6951ec52e19ac9a Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 24 Oct 2015 18:10:38 +0200 Subject: [PATCH 44/66] =?UTF-8?q?Adhesion=5Fconnexion=20fait=20appel=20?= =?UTF-8?q?=C3=A0=20crediter=20(notemment=20pour=20maj=20le=20solde=20quan?= =?UTF-8?q?d=20la=20cotis=20est=20pay=C3=A9e=20par=20solde...)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/objets.py b/objets.py index fcc2190..cc7494e 100644 --- a/objets.py +++ b/objets.py @@ -1770,15 +1770,7 @@ class facture(CransLdapObject): # On effectue les opérations do_adh_conn(self.proprio()) - self['recuPaiement'] = crans_utils.datetime_to_generalized_time_format(crans_utils.localized_datetime()) - self._recuPaiement = True - - # On force l'enregistrement de la facture après avoir crédité - if self.exists(): - self.save() - else: - self.create() - + self.crediter() def proprio(self, refresh=False): From dba07d8f60193d3cf568b83fa7a286fcdb2e5b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sat, 24 Oct 2015 18:18:26 +0200 Subject: [PATCH 45/66] Correction d'un bug sur l'attribut generalizedTimeFormat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * On avait une variable stamp qui ne devait plus y être. --- attributs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/attributs.py b/attributs.py index d6e9d2a..37dcb83 100644 --- a/attributs.py +++ b/attributs.py @@ -648,7 +648,7 @@ class generalizedTimeFormat(Attr): une donnée de temps suivant la RFC 4517 """ - __slots__ = ("stamp", "datetime",) + __slots__ = ("datetime",) python_type = datetime.datetime default = datetime_from_generalized_time_format("19700101000000Z") @@ -708,7 +708,6 @@ class generalizedTimeFormat(Attr): self.value = datetime_from_generalized_time_format(gtf) elif isinstance(gtf, datetime.datetime): self.value = gtf - self.stamp = from_generalized_time_format(self.value) @crans_attribute class debutAdhesion(generalizedTimeFormat): From f61aebd21a8ee6040da310656aaf6335b8b74e76 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 26 Oct 2015 15:36:27 +0100 Subject: [PATCH 46/66] =?UTF-8?q?Fix=20si=20jamais=20le=20canonical=20alia?= =?UTF-8?q?s=20est=20deja=20pris,=20calias=20=3D=20login=20+=20u'@crans.or?= =?UTF-8?q?g'=20et=20si=20jamais=20le=20mdp=20n'est=20pas=20pr=C3=A9cis?= =?UTF-8?q?=C3=A9=20=C3=A0=20la=20fonction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/objets.py b/objets.py index cc7494e..0e95dd8 100644 --- a/objets.py +++ b/objets.py @@ -969,11 +969,14 @@ class proprio(CransLdapObject): self['mail'] = [login + u"@crans.org"] calias = crans_utils.strip_spaces(fn) + u'.' + crans_utils.strip_spaces(ln) + '@crans.org' if crans_utils.mailexist(calias): - calias = login + calias = login + u'@crans.org' + if crans_utils.mailexist(calias): + raise ValueError('Creation impossible, Alias canonique déjà pris, merci de choisir un autre login') self['canonicalAlias'] = [calias] self['cn'] = [fn + u' ' + ln] self['loginShell'] = [unicode(shell)] - self['userPassword'] = [unicode(hash_pass)] + if hash_pass: + self['userPassword'] = [unicode(hash_pass)] self["solde"] = 0.0 if uidNumber: From 2becf821d3d63469a2fd3dd012bc579674c5cd1e Mon Sep 17 00:00:00 2001 From: Daniel STAN Date: Wed, 28 Oct 2015 17:42:17 +0100 Subject: [PATCH 47/66] readonly_password is None => ask secrets --- lc_ldap.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lc_ldap.py b/lc_ldap.py index f557b50..ff0a622 100644 --- a/lc_ldap.py +++ b/lc_ldap.py @@ -58,6 +58,7 @@ if "/usr/scripts" not in sys.path: sys.path.append('/usr/scripts') import gestion.config as config +from gestion import secrets_new as secrets import cranslib.deprecated # A priori, ldif_to_uldif et ldif_to_cldif sont obsolètes, @@ -105,6 +106,10 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): # Si un username, on récupère le dn associé… if user and not dn: + if readonly_dn is None: + readonly_dn = secrets.get('ldap_readonly_auth_dn') + if readonly_password is None: + readonly_password = secrets.get('ldap_readonly_password') dn = self.user_to_dn(user, readonly_dn, readonly_password) # Si on a un dn, on se connecte avec à la base ldap sinon on s'y From 990f791298cf294edaf52109c1ebbb4ee940b4a6 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 31 Oct 2015 12:55:19 +0100 Subject: [PATCH 48/66] =?UTF-8?q?Permet=20la=20cr=C3=A9ation=20de=20compte?= =?UTF-8?q?=20Crans=20quand=20l'objet=20est=20un=20club?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/objets.py b/objets.py index 0e95dd8..6d4d673 100644 --- a/objets.py +++ b/objets.py @@ -948,7 +948,6 @@ class proprio(CransLdapObject): return self['uid'][0] elif login: - fn = crans_utils.strip_accents(unicode(self['prenom'][0]).capitalize()) ln = crans_utils.strip_accents(unicode(self['nom'][0]).capitalize()) login = crans_utils.strip_spaces(crans_utils.strip_accents(login), by=u'-').lower() if not re.match('^[a-z][-a-z]{1,15}$', login): @@ -956,24 +955,40 @@ class proprio(CransLdapObject): 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 self.ldap_name == u'adherent': - if os.path.exists("/var/mail/" + login): - raise ValueError('Création du compte impossible : /var/mail/%s existant' % str(login)) + home = u'/home/' + login[0] + '/' + login + if os.path.exists(home): + raise ValueError('Création du compte impossible : home existant') - self._modifs['objectClass'] = [u'adherent', u'cransAccount', u'posixAccount', u'shadowAccount'] - self['uid'] = [login] - self['homeDirectory'] = [home] - self['mail'] = [login + u"@crans.org"] - calias = crans_utils.strip_spaces(fn) + u'.' + crans_utils.strip_spaces(ln) + '@crans.org' - if crans_utils.mailexist(calias): - calias = login + u'@crans.org' + if os.path.exists("/home/mail/" + login): + raise ValueError('Création du compte impossible : /home/mail/%s existant' % str(login)) + + fn = crans_utils.strip_accents(unicode(self['prenom'][0]).capitalize()) + self._modifs['objectClass'] = [u'adherent', u'cransAccount', u'posixAccount', u'shadowAccount'] + self['homeDirectory'] = [home] + self['cn'] = [fn + u' ' + ln] + self['mail'] = [login + u"@crans.org"] + calias = crans_utils.strip_spaces(fn) + u'.' + crans_utils.strip_spaces(ln) + '@crans.org' if crans_utils.mailexist(calias): - raise ValueError('Creation impossible, Alias canonique déjà pris, merci de choisir un autre login') - self['canonicalAlias'] = [calias] - self['cn'] = [fn + u' ' + ln] + calias = login + u'@crans.org' + if crans_utils.mailexist(calias): + raise ValueError('Creation impossible, Alias canonique déjà pris, merci de choisir un autre login') + self['canonicalAlias'] = [calias] + + else: + # C'est un club + home = u'/home/c/' + login + if os.path.exists(home): + raise ValueError('Création du compte impossible : home existant') + if os.path.exists("/home/mail/" + login): + raise ValueError('Création du compte impossible : /home/mail/%s existant' % str(login)) + self._modifs['objectClass'] = [u'club', u'cransAccount', u'posixAccount', u'shadowAccount'] + self['homeDirectory'] = [home] + self['cn'] = [ln] + + # Les attributs communs + self['uid'] = [login] self['loginShell'] = [unicode(shell)] if hash_pass: self['userPassword'] = [unicode(hash_pass)] From c0d2e04cc790a5ed1585dbb6be07b98b689e0635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 1 Nov 2015 03:47:56 +0100 Subject: [PATCH 49/66] =?UTF-8?q?Les=20homes=20sont=20g=C3=A9r=C3=A9s=20?= =?UTF-8?q?=C3=A0=20la=20main?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services.py | 1 - 1 file changed, 1 deletion(-) diff --git a/services.py b/services.py index 1627277..2a07809 100644 --- a/services.py +++ b/services.py @@ -32,7 +32,6 @@ services_to_attrs['mail_modif'] = [ attributs.droits, attributs.exempt ] + servi services_to_objects={} services_to_objects['delete']={} services_to_objects['create']={} -services_to_objects['create']['home'] = [objets.adherent, objets.club] services_to_objects['delete']['del_user'] = [objets.adherent, objets.club] NOW = time.time() From 00f67697ee7c4312e4dd5f56d4782c23c30b4f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 1 Nov 2015 04:15:30 +0100 Subject: [PATCH 50/66] check_uniqueness ne se comportait pas mieux qu'avant d20fa54614d0 --- attributs.py | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/attributs.py b/attributs.py index 37dcb83..e250737 100644 --- a/attributs.py +++ b/attributs.py @@ -756,25 +756,30 @@ class mail(rightProtectedAttr): attr = self.__class__.__name__ if str(self) in liste_exclue: return - if attr in ["mailAlias", "canonicalAlias", 'mail']: + if attr in ["mailAlias", "canonicalAlias", 'mail', 'mailExt']: mail = str(self) - # On commence par vérifier auprès du serveur SMTP - # si l'adresse email est prise sur le réseau. Cela permet - # de tester aussi les adresses statiquement écrites dans - # aliases.db. - try: - smtp = smtplib.SMTP(smtpserv) - smtp.putcmd("vrfy", mail) - res = smtp.getreply()[0] in [250, 252] - smtp.close() - except: - raise ValueError('Serveur de mail injoignable') - # Si le SMTP n'est pas joignable, on joue à la roulette - # russe en demandant à LDAP, sans vérifier les aliases - # statiques… - check = self.conn.search(u'mail=%s' % mail) - if len(check) >= 1: - res = True + + _, domain = mail.split('@') + if domain in config.dns.mail_crans: + # On commence par vérifier auprès du serveur SMTP + # si l'adresse email est prise sur le réseau. Cela permet + # de tester aussi les adresses statiquement écrites dans + # aliases.db. + try: + smtp = smtplib.SMTP(smtpserv) + smtp.putcmd("vrfy", mail) + res = smtp.getreply()[0] in [250, 252] + smtp.close() + except: + print 'Serveur de mail injoignable' + + check = self.conn.search( + u'(|(mail=%(mail)s)(mailAlias=%(mail)s)(canonicalAlias=%(mail)s)(mailExt=%(mail)s))' % { + 'mail': mail, + } + ) + if len(check) >= 1: + res = True if res: raise ValueError("Le mail %s est déjà pris." % (str(self))) @@ -1614,6 +1619,7 @@ class gpgMail(mail): legend = "Mail associé à une clef gpg" can_modify = [soi, nounou] ldap_name = "gpgMail" + def check_uniqueness(self, liste_exclue): super(mail, self).check_uniqueness(liste_exclue) From 443876863f0796ba55de2586a7f82c096e51efac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Sun, 1 Nov 2015 04:19:52 +0100 Subject: [PATCH 51/66] mail.check_uniqueness: Test initial inutile et initialisation de res --- attributs.py | 53 ++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/attributs.py b/attributs.py index e250737..f6f7b97 100644 --- a/attributs.py +++ b/attributs.py @@ -754,35 +754,40 @@ class mail(rightProtectedAttr): def check_uniqueness(self, liste_exclue): attr = self.__class__.__name__ + if str(self) in liste_exclue: return - if attr in ["mailAlias", "canonicalAlias", 'mail', 'mailExt']: - mail = str(self) - _, domain = mail.split('@') - if domain in config.dns.mail_crans: - # On commence par vérifier auprès du serveur SMTP - # si l'adresse email est prise sur le réseau. Cela permet - # de tester aussi les adresses statiquement écrites dans - # aliases.db. - try: - smtp = smtplib.SMTP(smtpserv) - smtp.putcmd("vrfy", mail) - res = smtp.getreply()[0] in [250, 252] - smtp.close() - except: - print 'Serveur de mail injoignable' + # On initialise le résultat, s'il vaut true en fin de course, le mail + # est déjà pris. + res = False - check = self.conn.search( - u'(|(mail=%(mail)s)(mailAlias=%(mail)s)(canonicalAlias=%(mail)s)(mailExt=%(mail)s))' % { - 'mail': mail, - } - ) - if len(check) >= 1: - res = True + mail = str(self) - if res: - raise ValueError("Le mail %s est déjà pris." % (str(self))) + _, domain = mail.split('@') + if domain in config.dns.mail_crans: + # On commence par vérifier auprès du serveur SMTP + # si l'adresse email est prise sur le réseau. Cela permet + # de tester aussi les adresses statiquement écrites dans + # aliases.db. + try: + smtp = smtplib.SMTP(smtpserv) + smtp.putcmd("vrfy", mail) + res = smtp.getreply()[0] in [250, 252] + smtp.close() + except: + print 'Serveur de mail injoignable' + + check = self.conn.search( + u'(|(mail=%(mail)s)(mailAlias=%(mail)s)(canonicalAlias=%(mail)s)(mailExt=%(mail)s))' % { + 'mail': mail, + } + ) + if len(check) >= 1: + res = True + + if res: + raise ValueError("Le mail %s est déjà pris." % (str(self))) def parse_value(self, mail): if not re.match(u'^[-+_.0-9A-Za-z]+@([A-Za-z0-9]{1}[A-Za-z0-9-_]+[.])+[a-z]{2,20}$', mail): From 8b5614214ceb9a3062b70670ef046995207e9475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Mon, 2 Nov 2015 15:25:29 +0100 Subject: [PATCH 52/66] =?UTF-8?q?L'attribut=20cn=20est=20modifiable=20par?= =?UTF-8?q?=20un=20c=C3=A2bleur?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attributs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/attributs.py b/attributs.py index f6f7b97..912fccd 100644 --- a/attributs.py +++ b/attributs.py @@ -1637,6 +1637,7 @@ class cn(Attr): __slots__ = () singlevalue = True optional = False + can_modify = [cableur, nounou] category = 'id' ldap_name = "cn" From 333ee38724484695cbeea44dd9398851f6d46351 Mon Sep 17 00:00:00 2001 From: Charlie Jacomme Date: Mon, 2 Nov 2015 23:22:16 +0100 Subject: [PATCH 53/66] Cableur can modify uid, uidNumber, gidNumber, gecos --- attributs.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/attributs.py b/attributs.py index 912fccd..1281d48 100644 --- a/attributs.py +++ b/attributs.py @@ -532,7 +532,7 @@ class uid(Attr): legend = u"L'identifiant canonique de l'adhérent" category = 'perso' unique = True - can_modify = [nounou] + can_modify = [cableur, nounou] ldap_name = "uid" @crans_attribute @@ -1514,6 +1514,7 @@ class uidNumber(intAttr): legend = "L'uid du compte de l'adherent" category = 'id' ldap_name = "uidNumber" + can_modify = [cableur, nounou] @crans_attribute class gidNumber(intAttr): @@ -1523,6 +1524,7 @@ class gidNumber(intAttr): legend = "Le gid du compte de l'adhérent" category = 'id' ldap_name = "gidNumber" + can_modify = [cableur, nounou] @crans_attribute class gecos(Attr): @@ -1532,6 +1534,7 @@ class gecos(Attr): legend = "Le gecos" category = 'id' ldap_name = "gecos" + can_modify = [cableur, nounou] @crans_attribute class userPassword(rightProtectedAttr): From bf0de1c5bee90864939668ab38b161b93484e074 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 3 Nov 2015 00:57:25 +0100 Subject: [PATCH 54/66] Pas d'initialisation du solde, pour eviter les prb d'acl entre droits cableur et imprimeur --- objets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/objets.py b/objets.py index 6d4d673..7ce0ed6 100644 --- a/objets.py +++ b/objets.py @@ -992,7 +992,6 @@ class proprio(CransLdapObject): self['loginShell'] = [unicode(shell)] if hash_pass: self['userPassword'] = [unicode(hash_pass)] - self["solde"] = 0.0 if uidNumber: if self.conn.search(u'(uidNumber=%s)' % uidNumber): From 440c9040bcfb1540d11074195a70531d0cf9e338 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Fri, 13 Nov 2015 01:03:09 +0100 Subject: [PATCH 55/66] Interval correct pour le calcul du la fin de connexion --- objets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/objets.py b/objets.py index 7ce0ed6..fd5fc26 100644 --- a/objets.py +++ b/objets.py @@ -1782,7 +1782,7 @@ class facture(CransLdapObject): self['debutConnexion'] = max(proprio.fin_connexion(), _now) con_month = max(proprio.fin_connexion(), _now).month con_year = max(proprio.fin_connexion(), _now).year - self['finConnexion'] = max(proprio.fin_connexion(), _now).replace(year=con_year + ((con_month + nbr_mois) // 12), month= (con_month + nbr_mois) % 12) + self['finConnexion'] = max(proprio.fin_connexion(), _now).replace(year=con_year + ((con_month + nbr_mois) // 12), month= (con_month + nbr_mois - 1 ) % 12 + 1) return # On effectue les opérations From 9b73ae0fde151e1a6e2887fc6e39d9f3f1e2ef82 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 14 Nov 2015 18:30:35 +0100 Subject: [PATCH 56/66] Quelques attribits par defaut pour les bornes (sinon ldap aime pas) --- attributs.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/attributs.py b/attributs.py index 1281d48..36b0518 100644 --- a/attributs.py +++ b/attributs.py @@ -1137,6 +1137,7 @@ class puissance(Attr): category = 'wifi' can_modify = [nounou] ldap_name = "puissance" + default = '60' @crans_attribute class canal(intAttr): @@ -1147,6 +1148,7 @@ class canal(intAttr): category = 'wifi' can_modify = [nounou] ldap_name = "canal" + default = "11" @crans_attribute class hotspot(boolAttr): @@ -1157,6 +1159,7 @@ class hotspot(boolAttr): category = 'wifi' can_modify = [nounou] ldap_name = "hotspot" + default = "FALSE" @crans_attribute class positionBorne(Attr): From 4a2a2f7e689fbbc7b0e32a53b7ad0bc466ee5988 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 14 Nov 2015 21:29:18 +0100 Subject: [PATCH 57/66] =?UTF-8?q?Attributs=20g=C3=A9ographiques=20=C3=A0?= =?UTF-8?q?=200=200=20par=20d=C3=A9faut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attributs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/attributs.py b/attributs.py index 36b0518..24a5c9f 100644 --- a/attributs.py +++ b/attributs.py @@ -1170,6 +1170,7 @@ class positionBorne(Attr): singlevalue = True optional = True ldap_name = "positionBorne" + default = "0 0" @crans_attribute class nvram(Attr): From 0733bfd99eec5775dcb9a638735715a8712c3dba Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 15 Nov 2015 14:56:49 +0100 Subject: [PATCH 58/66] Meilleure description de ce que fait l'attribu mailExt --- attributs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributs.py b/attributs.py index 24a5c9f..8951f61 100644 --- a/attributs.py +++ b/attributs.py @@ -836,7 +836,7 @@ class mailExt(mail): singlevalue = False optional = True unique = True - legend = u"Mail de secours" + legend = u"Mail de redirection ou de secours" can_modify = [soi, cableur, nounou] category = 'mail' ldap_name = "mailExt" From c4a2f1a71765837c4ed9bc3fd5d815432196ac8d Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 17 Nov 2015 18:29:33 +0100 Subject: [PATCH 59/66] Erreur dans le chemin du home de club --- objets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/objets.py b/objets.py index fd5fc26..56c5fe9 100644 --- a/objets.py +++ b/objets.py @@ -978,7 +978,7 @@ class proprio(CransLdapObject): else: # C'est un club - home = u'/home/c/' + login + home = u'/home/c/club/' + login.split('-', 1)[-1] if os.path.exists(home): raise ValueError('Création du compte impossible : home existant') if os.path.exists("/home/mail/" + login): From 37b30c32e59a10f388333235d69d7aa94702b9b9 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 18 Nov 2015 14:31:17 +0100 Subject: [PATCH 60/66] =?UTF-8?q?Les=20clubs=20peuvent=20etre=20d=C3=A9m?= =?UTF-8?q?=C3=A9nag=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attributs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributs.py b/attributs.py index 8951f61..ca8b76c 100644 --- a/attributs.py +++ b/attributs.py @@ -892,7 +892,7 @@ class chbre(Attr): def parse_value(self, chambre): if self.parent != None and u'club' in [str(o) for o in self.parent['objectClass']]: - if chambre in annuaires_pg.locaux_clubs(): + if chambre in annuaires_pg.locaux_clubs() or chambre in (u"EXT", u"????"): self.value = chambre else: raise ValueError("Club devrait etre en XclN, pas en %r" % chambre) From c644027d5f627360ee4b36b6d28cccd05a9da372 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Thu, 19 Nov 2015 15:13:59 +0100 Subject: [PATCH 61/66] Support du realm bornes v6 --- lc_ldap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lc_ldap.py b/lc_ldap.py index ff0a622..7ce859a 100644 --- a/lc_ldap.py +++ b/lc_ldap.py @@ -329,7 +329,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): uldif['objectClass'] = [u'machineCrans'] assert isinstance(owner, objets.AssociationCrans) - elif realm == "bornes": + elif realm in ["bornes", "bornes-v6"]: uldif['objectClass'] = [u'borneWifi'] assert isinstance(owner, objets.AssociationCrans) From ab13249ef5f3d28d4334204425ce9fb2de2a133f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Mon, 23 Nov 2015 18:53:39 +0100 Subject: [PATCH 62/66] =?UTF-8?q?mailExt=20et=20mailAlias=20h=C3=A9ritent?= =?UTF-8?q?=20de=20mail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attributs.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/attributs.py b/attributs.py index ca8b76c..1d194ea 100644 --- a/attributs.py +++ b/attributs.py @@ -816,7 +816,6 @@ class mailAlias(mail): optional = True unique = True legend = u"Alias mail" - can_modify = [soi, cableur, nounou] category = 'mail' ldap_name = "mailAlias" @@ -837,7 +836,6 @@ class mailExt(mail): optional = True unique = True legend = u"Mail de redirection ou de secours" - can_modify = [soi, cableur, nounou] category = 'mail' ldap_name = "mailExt" From 0b93ad0e193273dfac16886052d6d3917053912d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Mon, 23 Nov 2015 19:28:46 +0100 Subject: [PATCH 63/66] Les attributs uid/uidNumber/gidNumber sont rightprotected --- attributs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/attributs.py b/attributs.py index 1d194ea..02a5012 100644 --- a/attributs.py +++ b/attributs.py @@ -525,7 +525,7 @@ class aid(intAttr): self.value = int(aid) @crans_attribute -class uid(Attr): +class uid(rightProtectedAttr): __slots__ = () singlevalue = True option = False @@ -1491,7 +1491,7 @@ class homeDirectory(rightProtectedAttr): ldap_name = "homeDirectory" @crans_attribute -class loginShell(Attr): +class loginShell(rightProtectedAttr): __slots__ = () singlevalue = True optional = True @@ -1508,7 +1508,7 @@ class loginShell(Attr): self.value = shell @crans_attribute -class uidNumber(intAttr): +class uidNumber(intAttr, rightProtectedAttr): __slots__ = () singlevalue = True optional = True @@ -1519,7 +1519,7 @@ class uidNumber(intAttr): can_modify = [cableur, nounou] @crans_attribute -class gidNumber(intAttr): +class gidNumber(intAttr, rightProtectedAttr): __slots__ = () singlevalue = True optional = True From 9252614cd974c3c15e593007f532028473b1addf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Mon, 23 Nov 2015 19:29:10 +0100 Subject: [PATCH 64/66] =?UTF-8?q?Redondances=20avec=20le=20can=5Fmodify=20?= =?UTF-8?q?par=20d=C3=A9faut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- attributs.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/attributs.py b/attributs.py index 02a5012..fed20ac 100644 --- a/attributs.py +++ b/attributs.py @@ -1082,7 +1082,6 @@ class rid(intAttr): unique = True legend = u"Identifiant réseau de machine" category = 'id' - can_modify = [nounou] ldap_name = "rid" def parse_value(self, rid): @@ -1133,7 +1132,6 @@ class puissance(Attr): optional = True legend = u"puissance d'émission pour les bornes wifi" category = 'wifi' - can_modify = [nounou] ldap_name = "puissance" default = '60' @@ -1144,7 +1142,6 @@ class canal(intAttr): optional = True legend = u'Canal d\'émission de la borne' category = 'wifi' - can_modify = [nounou] ldap_name = "canal" default = "11" @@ -1155,7 +1152,6 @@ class hotspot(boolAttr): optional = True legend = u'Hotspot' category = 'wifi' - can_modify = [nounou] ldap_name = "hotspot" default = "FALSE" @@ -1164,7 +1160,6 @@ class positionBorne(Attr): __slots__ = () legend = u"Position de la borne" category = "wifi" - can_modify = [nounou] singlevalue = True optional = True ldap_name = "positionBorne" @@ -1175,7 +1170,6 @@ class nvram(Attr): __slots__ = () legend = u"Configuration speciale" optional = True - can_modify = [nounou] ldap_name = "nvram" class portAttr(Attr): @@ -1184,7 +1178,6 @@ class portAttr(Attr): optional = True legend = u'Ouverture de port' category = 'firewall' - can_modify = [nounou] def parse_value(self, port): if ":" in port: @@ -1258,7 +1251,6 @@ class nombrePrises(intAttr): singlevalue = True optional = True categoriy = 'base_tech' - can_modify = [nounou] ldap_name = "nombrePrises" @crans_attribute @@ -1268,7 +1260,6 @@ class prise(Attr): optional = True legend = u"Prise sur laquelle est branchée la machine" category = 'base_tech' - can_modify = [nounou] ldap_name = "prise" @crans_attribute @@ -1775,7 +1766,6 @@ class machineAlias(boolAttr): class issuerCN(Attr): __slots__ = () ldap_name = "issuerCN" - can_modify = [nounou] legend = "Common Name de l'éméteur du certificat" @crans_attribute @@ -1783,21 +1773,18 @@ class serialNumber(Attr): __slots__ = () ldap_name = "serialNumber" python_type = int - can_modify = [nounou] legend = "Numéro de série du certificat" @crans_attribute class start(intAttr): __slots__ = () ldap_name = "start" - can_modify = [nounou] legend = "Date de début" @crans_attribute class end(intAttr): __slots__ = () ldap_name = "end" - can_modify = [nounou] legend = "Date de fin" @crans_attribute @@ -1814,7 +1801,6 @@ class revocked(boolAttr): ldap_name = "revocked" singlevalue = True optional = True - can_modify = [nounou] legend = "Détermine si le certificat est révoqué" @crans_attribute From 962d0521982fb76999ee24059b2c729dbfaef932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Mon, 23 Nov 2015 19:29:22 +0100 Subject: [PATCH 65/66] Les blacklistes sont modifiables aussi par le droit bureau --- attributs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributs.py b/attributs.py index fed20ac..467c44e 100644 --- a/attributs.py +++ b/attributs.py @@ -1369,7 +1369,7 @@ class blacklist(Attr): optional = True legend = u"Blackliste" category = 'info' - can_modify = [nounou] + can_modify = [nounou, bureau] ldap_name = "blacklist" python_type = dict From 20c4c10624b46bb22868f84b6fd6eb36c107cf78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Elliott=20B=C3=A9cue?= Date: Mon, 23 Nov 2015 23:53:48 +0100 Subject: [PATCH 66/66] =?UTF-8?q?Le=20changement=20d'IP=20entra=C3=AEne=20?= =?UTF-8?q?un=20changement=20de=20rid,=20qui=20doit=20=C3=AAtre=20fait.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- objets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/objets.py b/objets.py index 56c5fe9..0be8e49 100644 --- a/objets.py +++ b/objets.py @@ -1384,7 +1384,7 @@ class machine(CransLdapObject): sbm['rid'] = (orid, nrid) # Les macAddress sont déjà des unicodes. # On change l'ip6 - elif old['macAddress'] != new['macAddress']: + if old['macAddress'] != new['macAddress']: nip6 = unicode(crans_utils.ip6_of_mac(new['macAddress'], new['rid'])) try: oip6 = unicode(self._modifs['ip6HostNumber'][0]) @@ -1418,6 +1418,7 @@ class machine(CransLdapObject): if unicode(self['ipHostNumber'][0]) != unicode(crans_utils.ip4_of_rid(sbm['rid'][1])): raise ValueError("L'ipv4 et le rid ne concordent pas !") self['ip6HostNumber'] = [unicode(crans_utils.ip6_of_mac(self['macAddress'][0].value, self['rid'][0].value))] + self['rid'] = [sbm['rid'][1]] if sbm['ipHostNumber']: if sbm['ipHostNumber'][1] == u"": ip4 = []