Compare commits

..

66 commits

Author SHA1 Message Date
Pierre-Elliott Bécue
20c4c10624 Le changement d'IP entraîne un changement de rid, qui doit être fait. 2015-11-23 23:53:48 +01:00
Pierre-Elliott Bécue
962d052198 Les blacklistes sont modifiables aussi par le droit bureau 2015-11-23 19:29:22 +01:00
Pierre-Elliott Bécue
9252614cd9 Redondances avec le can_modify par défaut 2015-11-23 19:29:10 +01:00
Pierre-Elliott Bécue
0b93ad0e19 Les attributs uid/uidNumber/gidNumber sont rightprotected 2015-11-23 19:28:46 +01:00
Pierre-Elliott Bécue
ab13249ef5 mailExt et mailAlias héritent de mail 2015-11-23 18:53:39 +01:00
Gabriel Detraz
c644027d5f Support du realm bornes v6 2015-11-19 15:13:59 +01:00
Gabriel Detraz
37b30c32e5 Les clubs peuvent etre déménagés 2015-11-18 14:31:17 +01:00
Gabriel Detraz
c4a2f1a717 Erreur dans le chemin du home de club 2015-11-17 18:29:33 +01:00
Gabriel Detraz
0733bfd99e Meilleure description de ce que fait l'attribu mailExt 2015-11-15 14:56:49 +01:00
Gabriel Detraz
4a2a2f7e68 Attributs géographiques à 0 0 par défaut 2015-11-14 21:29:18 +01:00
Gabriel Detraz
9b73ae0fde Quelques attribits par defaut pour les bornes (sinon ldap aime pas) 2015-11-14 18:30:35 +01:00
Gabriel Detraz
440c9040bc Interval correct pour le calcul du la fin de connexion 2015-11-13 01:03:09 +01:00
Gabriel Detraz
bf0de1c5be Pas d'initialisation du solde, pour eviter les prb d'acl entre droits cableur et imprimeur 2015-11-03 00:57:54 +01:00
Charlie Jacomme
333ee38724 Cableur can modify uid, uidNumber, gidNumber, gecos 2015-11-03 00:44:47 +01:00
Pierre-Elliott Bécue
8b5614214c L'attribut cn est modifiable par un câbleur 2015-11-02 15:25:55 +01:00
Pierre-Elliott Bécue
443876863f mail.check_uniqueness: Test initial inutile et initialisation de res 2015-11-01 04:19:52 +01:00
Pierre-Elliott Bécue
00f67697ee check_uniqueness ne se comportait pas mieux qu'avant d20fa54614 2015-11-01 04:15:30 +01:00
Pierre-Elliott Bécue
c0d2e04cc7 Les homes sont gérés à la main 2015-11-01 03:47:56 +01:00
Gabriel Detraz
990f791298 Permet la création de compte Crans quand l'objet est un club 2015-10-31 12:55:59 +01:00
Daniel STAN
2becf821d3 readonly_password is None => ask secrets 2015-10-28 17:43:02 +01:00
Gabriel Detraz
f61aebd21a Fix si jamais le canonical alias est deja pris, calias = login + u'@crans.org' et si jamais le mdp n'est pas précisé à la fonction 2015-10-26 15:38:31 +01:00
Pierre-Elliott Bécue
dba07d8f60 Correction d'un bug sur l'attribut generalizedTimeFormat
* On avait une variable stamp qui ne devait plus y être.
2015-10-24 18:18:26 +02:00
Gabriel Detraz
8b9b9dc169 Adhesion_connexion fait appel à crediter (notemment pour maj le solde quand la cotis est payée par solde...) 2015-10-24 18:10:38 +02:00
Gabriel Detraz
5b57476ab4 Nouvelle fonction de facture qui rempli les attributs finadh, debutadh , debu con et fin con sur une facture adh ou connexion 2015-10-23 19:24:44 +02:00
Pierre-Elliott Bécue
2a806bb367 Splitte oid entre les différents objets concernés 2015-10-18 21:01:05 +02:00
Pierre-Elliott Bécue
9388e1d217 CransLdapObject._id -> CransLdapObject.oid 2015-10-18 20:05:41 +02:00
Pierre-Elliott Bécue
d7800bbfb1 Fix "too many values to unpack" 2015-10-18 01:21:43 +02:00
Pierre-Elliott Bécue
91fa2974e4 Import inutilisé 2015-10-18 01:18:13 +02:00
Pierre-Elliott Bécue
3e71d60304 Bloc de code mort 2015-10-18 01:17:58 +02:00
Gabriel Detraz
bbea130179 Le cableur peut modifier l'ipsec, notemment à la creation de la machine 2015-10-11 16:29:28 +02:00
Pierre-Elliott Bécue
48de31425d Ajout de documentation sur is_modifiable dans userPassword 2015-10-08 15:09:04 +02:00
Pierre-Elliott Bécue
a1f27f9f1a Les timestamps c'est merdique, maintenant tout est du datetime 2015-10-03 14:09:08 +02:00
Pierre-Elliott Bécue
4de62cd0a8 Patch temporaire, le temps de transformer generalizedTimeFormat en datetime 2015-09-26 21:48:43 +02:00
Pierre-Elliott Bécue
af251f267b La date peut valoir None dans localized_datetime 2015-09-26 21:21:56 +02:00
Pierre-Elliott Bécue
8213b919ee Compatibilisation avec django 2015-09-26 03:01:59 +02:00
Pierre-Elliott Bécue
c18de85fd2 Enlève __unicode__ sur generalizedTimeFormat 2015-09-26 02:50:09 +02:00
Pierre-Elliott Bécue
9610cc6b0b Le nouveau type de recuPaiement n'est pas compatible avec != '' 2015-09-26 00:54:42 +02:00
Pierre-Elliott Bécue
d1eb555599 recuPaiement est un generalizedTimeFormat 2015-09-26 00:43:22 +02:00
Pierre-Elliott Bécue
dae802b057 Fonctionnalité pour générer un datetime timezone aware
* 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
2015-09-24 23:23:44 +02:00
Pierre-Elliott Bécue
4858953064 _slots doit être mis à jour quand on ajoute des attributs à un objet 2015-09-22 21:18:27 +02:00
Pierre-Elliott Bécue
854f774f37 Permet l'utilisation de datetimes avec generalizedTimeFormat 2015-09-22 21:16:56 +02:00
Pierre-Elliott Bécue
f23644497b DB_OVERRIDE vaut déjà localhost. On veut vo en usecase standard 2015-09-20 12:06:05 +02:00
Pierre-Elliott Bécue
930d09b539 Repasser en read-only après un passage en context n'est pas adapté 2015-09-20 04:26:44 +02:00
Pierre-Elliott Bécue
c91c89f3df Donc on peste contre les sites qui interdisent les + dans les emails ? 2015-09-10 14:58:53 +02:00
Pierre-Elliott Bécue
fa7387420a On passe le TLD pour la regexp mail à 20
* .cancerresearch fait déjà 14
2015-09-10 14:52:57 +02:00
Pierre-Elliott Bécue
d20fa54614 check_uniqueness sur les attributs de type mail corrigée 2015-09-10 12:54:17 +02:00
Valentin Samir
c35878562a Rafraichissement des factures par fin_connexion et fin_adhesion au plus une fois toutes les 1 min 2015-09-07 12:57:55 +02:00
Valentin Samir
4a0a514986 Remise à 1 appel ldap pour la fonction allMachinesAdherents 2015-09-07 12:50:39 +02:00
Pierre-Elliott Bécue
08cda773b0 proprio.__slots__ accueille une nouvelle variable 2015-09-06 19:56:02 +02:00
Pierre-Elliott Bécue
54a8d9c4cb On ne rafraîchit les factures au plus qu'une fois toutes les 5 sec 2015-09-06 19:30:16 +02:00
Pierre-Elliott Bécue
e59a468d19 La liste des droits basiques de la connexion ldap est encodée en unicode 2015-08-30 02:28:23 +02:00
Pierre-Elliott Bécue
83ca2e7991 Fait en sorte que l'unicode sandwich soit respecté dans tout history_gen 2015-08-27 12:27:40 +02:00
Pierre-Elliott Bécue
55eb87dce9 history_gen ne respectait pas l'unicode sandwich 2015-08-27 02:16:49 +02:00
Pierre-Elliott Bécue
0fb2cf4416 Plus d'infos sur les doublons dans une base ldap 2015-08-27 01:44:55 +02:00
Pierre-Elliott Bécue
4a3434847e En mode bref affiche deux colonnes, une pour adhésion et une pour connexion. 2015-08-26 19:06:46 +02:00
Pierre-Elliott Bécue
1353f00e17 Fait prendre un peu d'air au code 2015-08-26 18:43:41 +02:00
Pierre-Elliott Bécue
8ae5870468 Prise en charge des sshfp, et switches pour adresse/téléphone 2015-08-26 17:22:51 +02:00
Pierre-Elliott Bécue
12ba81e272 object.display est agnostique de ses arguments, qu'il forwarde à sprintf 2015-08-26 17:20:44 +02:00
Pierre-Elliott Bécue
ad8d9026f0 "Mettez des capot^Wespaces" 2015-08-25 02:38:12 +02:00
Pierre-Elliott Bécue
6142e6ef34 PEP8 pour la méthode total de facture 2015-08-24 21:03:06 +02:00
Pierre-Elliott Bécue
8f871ac166 Passe to_ldif en encodage de la base LDAP. 2015-08-23 14:28:39 +02:00
Pierre-Elliott Bécue
6c1dc6c587 repr ne retourne rien d'encodé 2015-08-23 14:01:56 +02:00
Pierre-Elliott Bécue
f988e962c9 Ajout de méthodes __unicode__ sur les objets 2015-08-18 04:16:02 +02:00
Daniel STAN
e46d1fe338 ajoute fin_connexion/adhesion_*datetime*
Parce que c'est plus facile à manipuler dans les templates.
2015-08-15 01:00:33 +02:00
Daniel STAN
5b66ae0095 /usr/scripts sans / final 2015-07-23 17:25:11 +02:00
Daniel STAN
f4318415e8 history_gen: garde login dans l'appel récursif 2015-07-23 16:29:56 +02:00
12 changed files with 660 additions and 301 deletions

View file

@ -47,7 +47,8 @@ import random
import string import string
from unicodedata import normalize 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 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 import itertools
if '/usr/scripts' not in sys.path: if '/usr/scripts' not in sys.path:
@ -242,7 +243,7 @@ class AttrsDict(dict):
""" """
ldif = {} ldif = {}
for attr, vals in self.items(): 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 return ldif
class Attr(object): class Attr(object):
@ -350,9 +351,7 @@ class Attr(object):
raise UniquenessError("%s déjà existant" % attr, [r.dn for r in res]) raise UniquenessError("%s déjà existant" % attr, [r.dn for r in res])
def is_modifiable(self, liste_droits): 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) return not set(liste_droits).isdisjoint(self.can_modify)
class AttributeFactory(object): class AttributeFactory(object):
@ -393,13 +392,26 @@ class rightProtectedAttr(Attr):
""" """
On permet la modification à un utilisateur 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: if not soi in liste_droits:
modifiables = set() 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: 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: if i in DROITS_SUPERVISEUR:
modifiables = modifiables.union(DROITS_SUPERVISEUR[i]) modifiables = modifiables.union(DROITS_SUPERVISEUR[i])
modifiables = list(modifiables) 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', []): for droit in self.parent.get('droits', []):
if droit not in modifiables and droit in TOUS_DROITS: if droit not in modifiables and droit in TOUS_DROITS:
return False return False
@ -513,14 +525,14 @@ class aid(intAttr):
self.value = int(aid) self.value = int(aid)
@crans_attribute @crans_attribute
class uid(Attr): class uid(rightProtectedAttr):
__slots__ = () __slots__ = ()
singlevalue = True singlevalue = True
option = False option = False
legend = u"L'identifiant canonique de l'adhérent" legend = u"L'identifiant canonique de l'adhérent"
category = 'perso' category = 'perso'
unique = True unique = True
can_modify = [nounou] can_modify = [cableur, nounou]
ldap_name = "uid" ldap_name = "uid"
@crans_attribute @crans_attribute
@ -636,25 +648,31 @@ class generalizedTimeFormat(Attr):
une donnée de temps suivant la RFC 4517 une donnée de temps suivant la RFC 4517
""" """
__slots__ = ("_stamp",) __slots__ = ("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): def __float__(self):
return self._stamp return from_generalized_time_format(unicode(self))
def __int__(self): def __int__(self):
return int(self._stamp) return int(float(self))
def __eq__(self, othertime): def __eq__(self, othertime):
if isinstance(othertime, generalizedTimeFormat): if isinstance(othertime, generalizedTimeFormat):
return self._stamp == othertime._stamp return self.value == othertime.value
elif isinstance(othertime, float): elif isinstance(othertime, float):
return self._stamp == othertime return float(self) == othertime
elif isinstance(othertime, int): elif isinstance(othertime, int):
return self._stamp == othertime return int(self) == othertime
elif isinstance(othertime, unicode) or isinstance(othertime, str): elif isinstance(othertime, unicode) or isinstance(othertime, str):
resource = generalizedTimeFormat(othertime, conn=None, Parent=None) resource = generalizedTimeFormat(othertime, conn=None, Parent=None)
return self._stamp == resource._stamp return self == resource
elif isinstance(othertime, datetime.datetime):
return self.value == othertime
else: else:
return False return False
@ -663,14 +681,16 @@ class generalizedTimeFormat(Attr):
def __lt__(self, othertime): def __lt__(self, othertime):
if isinstance(othertime, generalizedTimeFormat): if isinstance(othertime, generalizedTimeFormat):
return self._stamp < othertime._stamp return self.value < othertime.value
elif isinstance(othertime, float): elif isinstance(othertime, float):
return self._stamp < othertime return float(self) < othertime
elif isinstance(othertime, int): elif isinstance(othertime, int):
return self._stamp < othertime return int(self) < othertime
elif isinstance(othertime, unicode) or isinstance(othertime, str): elif isinstance(othertime, unicode) or isinstance(othertime, str):
resource = generalizedTimeFormat(othertime, conn=None, Parent=None) resource = generalizedTimeFormat(othertime, conn=None, Parent=None)
return self._stamp < resource._stamp return self < resource
elif isinstance(othertime, datetime.datetime):
return self.value < othertime
else: else:
return False return False
@ -685,15 +705,9 @@ class generalizedTimeFormat(Attr):
def parse_value(self, gtf): def parse_value(self, gtf):
if isinstance(gtf, str) or isinstance(gtf, unicode): if isinstance(gtf, str) or isinstance(gtf, unicode):
if not ('Z' in gtf or '+' in gtf or '-' in gtf): self.value = datetime_from_generalized_time_format(gtf)
self._stamp = gtf elif isinstance(gtf, datetime.datetime):
self.value = to_generalized_time_format(float(gtf))
else:
self._stamp = from_generalized_time_format(gtf)
self.value = gtf self.value = gtf
elif isinstance(gtf, float):
self._stamp = gtf
self.value = to_generalized_time_format(gtf)
@crans_attribute @crans_attribute
class debutAdhesion(generalizedTimeFormat): class debutAdhesion(generalizedTimeFormat):
@ -740,28 +754,43 @@ class mail(rightProtectedAttr):
def check_uniqueness(self, liste_exclue): def check_uniqueness(self, liste_exclue):
attr = self.__class__.__name__ attr = self.__class__.__name__
if str(self) in liste_exclue: if str(self) in liste_exclue:
return return
if attr in ["mailAlias", "canonicalAlias", 'mail']:
mail, end = str(self).split('@', 1) # On initialise le résultat, s'il vaut true en fin de course, le mail
if end.startswith('crans'): # est déjà pris.
res = False
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: try:
smtp = smtplib.SMTP(smtpserv) smtp = smtplib.SMTP(smtpserv)
smtp.putcmd("vrfy", mail) smtp.putcmd("vrfy", mail)
res = smtp.getreply()[0] in [250, 252] res = smtp.getreply()[0] in [250, 252]
smtp.close() smtp.close()
except: except:
raise ValueError('Serveur de mail injoignable') 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: if res:
raise ValueError("Le mail %s est déjà pris." % (str(self))) raise ValueError("Le mail %s est déjà pris." % (str(self)))
else:
check = self.conn.search(u'mail=%s' % mail)
if len(check) >= 1:
raise ValueError("Le mail %s est déjà pris." % (str(self)))
def parse_value(self, mail): 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)) raise ValueError("%s invalide %r" % (self.legend, mail))
self.value = mail self.value = mail
@ -787,7 +816,6 @@ class mailAlias(mail):
optional = True optional = True
unique = True unique = True
legend = u"Alias mail" legend = u"Alias mail"
can_modify = [soi, cableur, nounou]
category = 'mail' category = 'mail'
ldap_name = "mailAlias" ldap_name = "mailAlias"
@ -807,8 +835,7 @@ class mailExt(mail):
singlevalue = False singlevalue = False
optional = True optional = True
unique = True unique = True
legend = u"Mail de secours" legend = u"Mail de redirection ou de secours"
can_modify = [soi, cableur, nounou]
category = 'mail' category = 'mail'
ldap_name = "mailExt" ldap_name = "mailExt"
@ -863,7 +890,7 @@ class chbre(Attr):
def parse_value(self, chambre): def parse_value(self, chambre):
if self.parent != None and u'club' in [str(o) for o in self.parent['objectClass']]: 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 self.value = chambre
else: else:
raise ValueError("Club devrait etre en XclN, pas en %r" % chambre) raise ValueError("Club devrait etre en XclN, pas en %r" % chambre)
@ -1055,7 +1082,6 @@ class rid(intAttr):
unique = True unique = True
legend = u"Identifiant réseau de machine" legend = u"Identifiant réseau de machine"
category = 'id' category = 'id'
can_modify = [nounou]
ldap_name = "rid" ldap_name = "rid"
def parse_value(self, rid): def parse_value(self, rid):
@ -1087,7 +1113,7 @@ class ipsec(Attr):
legend = u'Clef wifi' legend = u'Clef wifi'
category = 'wifi' category = 'wifi'
ldap_name = "ipsec" ldap_name = "ipsec"
can_modify = [nounou, parent] can_modify = [nounou, cableur, parent]
historique = "info" historique = "info"
default = u'auto' default = u'auto'
@ -1106,8 +1132,8 @@ class puissance(Attr):
optional = True optional = True
legend = u"puissance d'émission pour les bornes wifi" legend = u"puissance d'émission pour les bornes wifi"
category = 'wifi' category = 'wifi'
can_modify = [nounou]
ldap_name = "puissance" ldap_name = "puissance"
default = '60'
@crans_attribute @crans_attribute
class canal(intAttr): class canal(intAttr):
@ -1116,8 +1142,8 @@ class canal(intAttr):
optional = True optional = True
legend = u'Canal d\'émission de la borne' legend = u'Canal d\'émission de la borne'
category = 'wifi' category = 'wifi'
can_modify = [nounou]
ldap_name = "canal" ldap_name = "canal"
default = "11"
@crans_attribute @crans_attribute
class hotspot(boolAttr): class hotspot(boolAttr):
@ -1126,25 +1152,24 @@ class hotspot(boolAttr):
optional = True optional = True
legend = u'Hotspot' legend = u'Hotspot'
category = 'wifi' category = 'wifi'
can_modify = [nounou]
ldap_name = "hotspot" ldap_name = "hotspot"
default = "FALSE"
@crans_attribute @crans_attribute
class positionBorne(Attr): class positionBorne(Attr):
__slots__ = () __slots__ = ()
legend = u"Position de la borne" legend = u"Position de la borne"
category = "wifi" category = "wifi"
can_modify = [nounou]
singlevalue = True singlevalue = True
optional = True optional = True
ldap_name = "positionBorne" ldap_name = "positionBorne"
default = "0 0"
@crans_attribute @crans_attribute
class nvram(Attr): class nvram(Attr):
__slots__ = () __slots__ = ()
legend = u"Configuration speciale" legend = u"Configuration speciale"
optional = True optional = True
can_modify = [nounou]
ldap_name = "nvram" ldap_name = "nvram"
class portAttr(Attr): class portAttr(Attr):
@ -1153,7 +1178,6 @@ class portAttr(Attr):
optional = True optional = True
legend = u'Ouverture de port' legend = u'Ouverture de port'
category = 'firewall' category = 'firewall'
can_modify = [nounou]
def parse_value(self, port): def parse_value(self, port):
if ":" in port: if ":" in port:
@ -1227,7 +1251,6 @@ class nombrePrises(intAttr):
singlevalue = True singlevalue = True
optional = True optional = True
categoriy = 'base_tech' categoriy = 'base_tech'
can_modify = [nounou]
ldap_name = "nombrePrises" ldap_name = "nombrePrises"
@crans_attribute @crans_attribute
@ -1237,7 +1260,6 @@ class prise(Attr):
optional = True optional = True
legend = u"Prise sur laquelle est branchée la machine" legend = u"Prise sur laquelle est branchée la machine"
category = 'base_tech' category = 'base_tech'
can_modify = [nounou]
ldap_name = "prise" ldap_name = "prise"
@crans_attribute @crans_attribute
@ -1347,7 +1369,7 @@ class blacklist(Attr):
optional = True optional = True
legend = u"Blackliste" legend = u"Blackliste"
category = 'info' category = 'info'
can_modify = [nounou] can_modify = [nounou, bureau]
ldap_name = "blacklist" ldap_name = "blacklist"
python_type = dict python_type = dict
@ -1460,7 +1482,7 @@ class homeDirectory(rightProtectedAttr):
ldap_name = "homeDirectory" ldap_name = "homeDirectory"
@crans_attribute @crans_attribute
class loginShell(Attr): class loginShell(rightProtectedAttr):
__slots__ = () __slots__ = ()
singlevalue = True singlevalue = True
optional = True optional = True
@ -1477,7 +1499,7 @@ class loginShell(Attr):
self.value = shell self.value = shell
@crans_attribute @crans_attribute
class uidNumber(intAttr): class uidNumber(intAttr, rightProtectedAttr):
__slots__ = () __slots__ = ()
singlevalue = True singlevalue = True
optional = True optional = True
@ -1485,15 +1507,17 @@ class uidNumber(intAttr):
legend = "L'uid du compte de l'adherent" legend = "L'uid du compte de l'adherent"
category = 'id' category = 'id'
ldap_name = "uidNumber" ldap_name = "uidNumber"
can_modify = [cableur, nounou]
@crans_attribute @crans_attribute
class gidNumber(intAttr): class gidNumber(intAttr, rightProtectedAttr):
__slots__ = () __slots__ = ()
singlevalue = True singlevalue = True
optional = True optional = True
legend = "Le gid du compte de l'adhérent" legend = "Le gid du compte de l'adhérent"
category = 'id' category = 'id'
ldap_name = "gidNumber" ldap_name = "gidNumber"
can_modify = [cableur, nounou]
@crans_attribute @crans_attribute
class gecos(Attr): class gecos(Attr):
@ -1503,6 +1527,7 @@ class gecos(Attr):
legend = "Le gecos" legend = "Le gecos"
category = 'id' category = 'id'
ldap_name = "gecos" ldap_name = "gecos"
can_modify = [cableur, nounou]
@crans_attribute @crans_attribute
class userPassword(rightProtectedAttr): class userPassword(rightProtectedAttr):
@ -1595,6 +1620,7 @@ class gpgMail(mail):
legend = "Mail associé à une clef gpg" legend = "Mail associé à une clef gpg"
can_modify = [soi, nounou] can_modify = [soi, nounou]
ldap_name = "gpgMail" ldap_name = "gpgMail"
def check_uniqueness(self, liste_exclue): def check_uniqueness(self, liste_exclue):
super(mail, self).check_uniqueness(liste_exclue) super(mail, self).check_uniqueness(liste_exclue)
@ -1607,6 +1633,7 @@ class cn(Attr):
__slots__ = () __slots__ = ()
singlevalue = True singlevalue = True
optional = False optional = False
can_modify = [cableur, nounou]
category = 'id' category = 'id'
ldap_name = "cn" ldap_name = "cn"
@ -1669,7 +1696,7 @@ class modePaiement(Attr):
self.value = mode self.value = mode
@crans_attribute @crans_attribute
class recuPaiement(Attr): class recuPaiement(generalizedTimeFormat):
__slots__ = () __slots__ = ()
ldap_name = "recuPaiement" ldap_name = "recuPaiement"
can_modify = [cableur, nounou] can_modify = [cableur, nounou]
@ -1739,7 +1766,6 @@ class machineAlias(boolAttr):
class issuerCN(Attr): class issuerCN(Attr):
__slots__ = () __slots__ = ()
ldap_name = "issuerCN" ldap_name = "issuerCN"
can_modify = [nounou]
legend = "Common Name de l'éméteur du certificat" legend = "Common Name de l'éméteur du certificat"
@crans_attribute @crans_attribute
@ -1747,21 +1773,18 @@ class serialNumber(Attr):
__slots__ = () __slots__ = ()
ldap_name = "serialNumber" ldap_name = "serialNumber"
python_type = int python_type = int
can_modify = [nounou]
legend = "Numéro de série du certificat" legend = "Numéro de série du certificat"
@crans_attribute @crans_attribute
class start(intAttr): class start(intAttr):
__slots__ = () __slots__ = ()
ldap_name = "start" ldap_name = "start"
can_modify = [nounou]
legend = "Date de début" legend = "Date de début"
@crans_attribute @crans_attribute
class end(intAttr): class end(intAttr):
__slots__ = () __slots__ = ()
ldap_name = "end" ldap_name = "end"
can_modify = [nounou]
legend = "Date de fin" legend = "Date de fin"
@crans_attribute @crans_attribute
@ -1778,7 +1801,6 @@ class revocked(boolAttr):
ldap_name = "revocked" ldap_name = "revocked"
singlevalue = True singlevalue = True
optional = True optional = True
can_modify = [nounou]
legend = "Détermine si le certificat est révoqué" legend = "Détermine si le certificat est révoqué"
@crans_attribute @crans_attribute

View file

@ -47,7 +47,6 @@ if '/usr/scripts' not in sys.path:
sys.path.append('/usr/scripts') sys.path.append('/usr/scripts')
from gestion import config from gestion import config
from unicodedata import normalize from unicodedata import normalize
import subprocess
from netifaces import interfaces, ifaddresses, AF_INET from netifaces import interfaces, ifaddresses, AF_INET
try: try:
@ -55,6 +54,11 @@ try:
except: except:
pytz = None pytz = None
try:
import dateutil.tz
except:
dateutil = None
DEVNULL = open(os.devnull, 'w') DEVNULL = open(os.devnull, 'w')
def find_rid_plage(rid): def find_rid_plage(rid):
@ -245,7 +249,6 @@ def escape(chaine):
dans une requête ldap.""" dans une requête ldap."""
return ldap.filter.escape_filter_chars(chaine) return ldap.filter.escape_filter_chars(chaine)
def hash_password(password, salt=None, longueur=4): def hash_password(password, salt=None, longueur=4):
if longueur < 4: if longueur < 4:
raise ValueError("salt devrait faire au moins 4 octets") raise ValueError("salt devrait faire au moins 4 octets")
@ -341,21 +344,57 @@ 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")) 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): def localized_datetime(date=None, tz=None):
"""Returns a datetime from generalized time format """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 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)
# 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: if '-' in gtf or '+' in gtf:
date, tz = gtf[0:14], gtf[14:] date, tz = gtf[0:14], gtf[14:]
else: else:
date = gtf.replace("Z", '') date = gtf.replace("Z", '')
tz = '+0000' tz = '+0000'
the_date = datetime.datetime.strptime(date, "%Y%m%d%H%M%S") return localized_datetime(date, tz)
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
def datetime_to_generalized_time_format(datetime_obj): def datetime_to_generalized_time_format(datetime_obj):
"""Transforms a datetime to a GTF""" """Transforms a datetime to a GTF"""

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# LC_LDAP.PY-- LightWeight CransLdap # LC_LDAP.PY-- LightWeight CransLdap
@ -39,6 +38,7 @@
import os import os
import sys import sys
import re import re
import time
from contextlib import contextmanager from contextlib import contextmanager
import ldap import ldap
@ -53,11 +53,12 @@ import variables
import copy import copy
import itertools import itertools
## import de /usr/scripts/ ## import de /usr/scripts
if not "/usr/scripts/" in sys.path: if "/usr/scripts" not in sys.path:
sys.path.append('/usr/scripts/') sys.path.append('/usr/scripts')
import gestion.config as config import gestion.config as config
from gestion import secrets_new as secrets
import cranslib.deprecated import cranslib.deprecated
# A priori, ldif_to_uldif et ldif_to_cldif sont obsolètes, # 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é… # Si un username, on récupère le dn associé…
if user and not dn: 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) 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 # Si on a un dn, on se connecte avec à la base ldap sinon on s'y
@ -112,7 +117,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object):
if dn: if dn:
self.conn = self.bind_s(dn, cred) self.conn = self.bind_s(dn, cred)
self.dn = dn 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: if dn == variables.admin_dn:
self.droits += [attributs.nounou] self.droits += [attributs.nounou]
# Il faut peupler current_login, qui sera utilisé pour écrire dans l'historique qui fait des modifications # Il faut peupler current_login, qui sera utilisé pour écrire dans l'historique qui fait des modifications
@ -247,6 +252,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object):
res = {} res = {}
parent = {} parent = {}
machines = {} machines = {}
factures = {}
# (proxying de la base ldap) # (proxying de la base ldap)
for dn, attrs in self.search_s(variables.base_dn, scope=2): for dn, attrs in self.search_s(variables.base_dn, scope=2):
# On crée les listes des machines et propriétaires # On crée les listes des machines et propriétaires
@ -256,14 +262,26 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object):
if not machines.has_key(parent_dn): if not machines.has_key(parent_dn):
machines[parent_dn] = [] machines[parent_dn] = []
machines[parent_dn].append(m) 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): 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)) parent[dn] = objets.new_cransldapobject(self, dn, mode, uldif=ldif_to_uldif(attrs))
allmachines = [] 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]._machines = mlist
parent[dn]._factures = flist
parent[dn]._factures_last_update = time.time()
for m in mlist: for m in mlist:
m._proprio = parent[dn] m._proprio = parent[dn]
allmachines.append(m) 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) return allmachines, parent.values() # on renvoie la liste des machines et des adherents (dont club et crans)
def allMachines(self, mode='ro'): def allMachines(self, mode='ro'):
@ -311,7 +329,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object):
uldif['objectClass'] = [u'machineCrans'] uldif['objectClass'] = [u'machineCrans']
assert isinstance(owner, objets.AssociationCrans) assert isinstance(owner, objets.AssociationCrans)
elif realm == "bornes": elif realm in ["bornes", "bornes-v6"]:
uldif['objectClass'] = [u'borneWifi'] uldif['objectClass'] = [u'borneWifi']
assert isinstance(owner, objets.AssociationCrans) assert isinstance(owner, objets.AssociationCrans)

View file

@ -179,7 +179,7 @@ class LdapLockHolder:
host, pid, begin = self.getlock(item, value) host, pid, begin = self.getlock(item, value)
time_left = self.timeout - (time.time() - begin) time_left = self.timeout - (time.time() - begin)
if time_left <= delai: 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): def removelock(self, item, value, Id='default', force=False):
""" """
@ -214,6 +214,3 @@ class LdapLockHolder:
except ldap.INVALID_DN_SYNTAX: except ldap.INVALID_DN_SYNTAX:
print '%s=%s,%s' % (item, value, LOCKS_DN) print '%s=%s,%s' % (item, value, LOCKS_DN)
raise raise
except ValueError as e:
self.removelock(item, value, Id, force=True)
raise LockNotFound()

446
objets.py
View file

@ -56,9 +56,9 @@ import ldap_locks
import variables import variables
import printing import printing
## import de /usr/scripts/ ## import de /usr/scripts
if not "/usr/scripts/" in sys.path: if "/usr/scripts" not in sys.path:
sys.path.append('/usr/scripts/') sys.path.append('/usr/scripts')
import gestion.config as config import gestion.config as config
import gestion.config.impression import gestion.config.impression
@ -66,6 +66,7 @@ import cranslib.deprecated
#: Champs à ignorer dans l'historique #: Champs à ignorer dans l'historique
HIST_IGNORE_FIELDS = ["modifiersName", "entryCSN", "modifyTimestamp", "historique"] HIST_IGNORE_FIELDS = ["modifiersName", "entryCSN", "modifyTimestamp", "historique"]
FACTURES_REFRESH_PERIOD = 60
def new_cransldapobject(conn, dn, mode='ro', uldif=None, lockId=None): def new_cransldapobject(conn, dn, mode='ro', uldif=None, lockId=None):
"""Crée un objet :py:class:`CransLdapObject` en utilisant la classe correspondant à """Crée un objet :py:class:`CransLdapObject` en utilisant la classe correspondant à
@ -100,7 +101,8 @@ class CransLdapObject(object):
""" Qui peut faire quoi ? """ """ Qui peut faire quoi ? """
__slots__ = ("in_context", "conn", "lockId", "attrs", "_modifs", "dn", "parent_dn", "mode") __slots__ = ("in_context", "conn", "lockId", "attrs", "_modifs", "dn", "parent_dn", "mode")
can_be_by = { variables.created: [attributs.nounou], can_be_by = {
variables.created: [attributs.nounou],
variables.modified: [attributs.nounou], variables.modified: [attributs.nounou],
variables.deleted: [attributs.nounou], variables.deleted: [attributs.nounou],
} }
@ -205,18 +207,9 @@ class CransLdapObject(object):
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) 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""" """Retourne l'id de l'objet courant"""
if isinstance(self, adherent): return [self.dn.split(",")[0].split("=")[1]]
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]
def _out_of_context(self, *args, **kwargs): def _out_of_context(self, *args, **kwargs):
raise EnvironmentError("Hors du context, impossible de faire des écritures") raise EnvironmentError("Hors du context, impossible de faire des écritures")
@ -228,8 +221,6 @@ class CransLdapObject(object):
#self.save = self._out_of_context #self.save = self._out_of_context
#self.create = self._out_of_context #self.create = self._out_of_context
#self.delete = 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 # On purge les lock de l'objet
self.conn.lockholder.purge(self.lockId) self.conn.lockholder.purge(self.lockId)
@ -295,8 +286,8 @@ class CransLdapObject(object):
else: else:
return self.attrs.items() return self.attrs.items()
def display(self, historique=5, blacklist=5): def display(self, *args, **kwargs):
print printing.sprint(self, historique=historique, blacklist=blacklist) print printing.sprint(self, *args, **kwargs)
def history_add(self, login, chain): def history_add(self, login, chain):
"""Ajoute une ligne à l'historique de l'objet. """Ajoute une ligne à l'historique de l'objet.
@ -312,7 +303,8 @@ class CransLdapObject(object):
"Génère une ligne d'historique pour l'arribut attr ou une ligne par attributs pour l'objet courant" "Génère une ligne d'historique pour l'arribut attr ou une ligne par attributs pour l'objet courant"
if attr is None: if attr is None:
for attr in self.keys(): 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): def partial_name(name, max_len=14, start=7, end=7):
if len(name) > max_len: if len(name) > max_len:
return "%s%s" % (name[:start], name[-end:]) return "%s%s" % (name[:start], name[-end:])
@ -343,8 +335,8 @@ class CransLdapObject(object):
if attr.historique == "full": if attr.historique == "full":
comm = u"%s (%s -> %s)" % (attr.ldap_name, old_values[0], new_values[0]) comm = u"%s (%s -> %s)" % (attr.ldap_name, old_values[0], new_values[0])
elif attr.historique == "partial": elif attr.historique == "partial":
old = partial_name(str(old_values[0])) old = partial_name(unicode(old_values[0]))
new = partial_name(str(new_values[0])) new = partial_name(unicode(new_values[0]))
comm = u"%s (%s -> %s)" % (attr.ldap_name, old, new) comm = u"%s (%s -> %s)" % (attr.ldap_name, old, new)
elif attr.historique == "info": elif attr.historique == "info":
comm = u"%s" % attr.ldap_name comm = u"%s" % attr.ldap_name
@ -353,7 +345,7 @@ class CransLdapObject(object):
if attr.historique == "info": if attr.historique == "info":
comm = u"+%s" % attr.ldap_name comm = u"+%s" % attr.ldap_name
elif attr.historique in ["full", "partial"]: elif attr.historique in ["full", "partial"]:
new = str(new_values[0]) new = unicode(new_values[0])
if attr.historique == "partial": if attr.historique == "partial":
new = partial_name(new) new = partial_name(new)
comm = u"%s+%s" % (attr.ldap_name, new) comm = u"%s+%s" % (attr.ldap_name, new)
@ -362,7 +354,7 @@ class CransLdapObject(object):
if attr.historique == "info": if attr.historique == "info":
comm = u"-%s" % attr.ldap_name comm = u"-%s" % attr.ldap_name
elif attr.historique in ["full", "partial"]: elif attr.historique in ["full", "partial"]:
old = str(old_values[0]) old = unicode(old_values[0])
if attr.historique == "partial": if attr.historique == "partial":
old = partial_name(old) old = partial_name(old)
comm = u"%s-%s" % (attr.ldap_name, old) comm = u"%s-%s" % (attr.ldap_name, old)
@ -370,9 +362,9 @@ class CransLdapObject(object):
added = [] added = []
deleted = [] deleted = []
if attr.historique == "partial": if attr.historique == "partial":
append = lambda x: partial_name(str(x)) append = lambda x: partial_name(unicode(x))
else: else:
append = lambda x: str(x) append = lambda x: unicode(x)
for a in new_values: for a in new_values:
if not a in old_values: if not a in old_values:
added.append(append(a)) added.append(append(a))
@ -453,7 +445,7 @@ class CransLdapObject(object):
ldif = self._modifs.to_ldif() ldif = self._modifs.to_ldif()
for attr in binary: for attr in binary:
ldif['%s;binary' % attr] = ldif[attr] ldif['%s;binary' % attr] = ldif[attr]
del(ldif[attr]) del ldif[attr]
# Création de la requête LDAP # Création de la requête LDAP
modlist = addModlist(ldif) modlist = addModlist(ldif)
# Requête LDAP de création de l'objet # Requête LDAP de création de l'objet
@ -592,11 +584,11 @@ class CransLdapObject(object):
ldif = self._modifs.to_ldif() ldif = self._modifs.to_ldif()
orig_ldif = self.attrs.to_ldif() orig_ldif = self.attrs.to_ldif()
for attr in binary: for attr in binary:
ldif['%s;binary' % attr]=ldif[attr] ldif['%s;binary' % (attr,)] = ldif[attr]
orig_ldif['%s;binary' % attr]=orig_ldif.get(attr, []) orig_ldif['%s;binary' % (attr,)] = orig_ldif.get(attr, [])
del(ldif[attr]) del ldif[attr]
try: try:
del(orig_ldif[attr]) del orig_ldif[attr]
except KeyError: except KeyError:
pass pass
@ -678,8 +670,11 @@ class CransLdapObject(object):
try: try:
for attribut in attrs_before_verif: for attribut in attrs_before_verif:
# Vérification que (attr, value) est localement unique # 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: 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 # 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: if not no_uniq_lock and attribut.unique and not attribut in self._modifs.get(attr, []) and not attribut in attribut.unique_exclue:
@ -847,31 +842,51 @@ def crans_object(classe):
class InetOrgPerson(CransLdapObject): class InetOrgPerson(CransLdapObject):
__slots__ = () __slots__ = ()
ldap_name = "inetOrgPerson" ldap_name = "inetOrgPerson"
def __unicode__(self):
return u"%s : cn=%s" % (self.__class__.__name__, self['cn'][0])
def __repr__(self): def __repr__(self):
return str(self.__class__.__name__) + " : cn=" + str(self['cn'][0]) return repr(self.__unicode__())
pass pass
class proprio(CransLdapObject): class proprio(CransLdapObject):
u""" Un propriétaire de machine (adhérent, club…) """ 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, attributs.bureau, attributs.cableur], can_be_by = {
variables.modified: [attributs.nounou, attributs.bureau, attributs.soi, attributs.cableur], variables.created: [
variables.deleted: [attributs.nounou, attributs.bureau,], 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, crans_account_attribs = [
attributs.uid, attributs.canonicalAlias, attributs.solde,
attributs.contourneGreylist, attributs.derniereConnexion, attributs.contourneGreylist, attributs.derniereConnexion,
attributs.homepageAlias, attributs.loginShell, attributs.gecos, attributs.homepageAlias, attributs.loginShell, attributs.gecos,
attributs.uidNumber, attributs.homeDirectory, attributs.uidNumber, attributs.homeDirectory,
attributs.gidNumber, attributs.userPassword, attributs.gidNumber, attributs.userPassword,
attributs.mailAlias, attributs.cn, attributs.rewriteMailHeaders, attributs.mailAlias, attributs.cn, attributs.rewriteMailHeaders,
attributs.mailExt, attributs.compteWiki, attributs.droits, attributs.mailExt, attributs.compteWiki, attributs.droits,
attributs.shadowExpire] attributs.shadowExpire,
default_attribs = [attributs.nom, attributs.chbre, attributs.paiement, attributs.info, ]
default_attribs = [
attributs.nom, attributs.chbre, attributs.paiement, attributs.info,
attributs.blacklist, attributs.controle, attributs.historique, attributs.blacklist, attributs.controle, attributs.historique,
attributs.debutAdhesion, attributs.finAdhesion, attributs.debutConnexion, attributs.debutAdhesion, attributs.finAdhesion, attributs.debutConnexion,
attributs.finConnexion] attributs.finConnexion,
]
@property @property
def attribs(self): def attribs(self):
@ -888,13 +903,17 @@ class proprio(CransLdapObject):
"""Renvoie la liste des clubs dont l'adherent est imprimeur (surchargée dans les objets adherent)""" """Renvoie la liste des clubs dont l'adherent est imprimeur (surchargée dans les objets adherent)"""
return [] return []
def __unicode__(self):
return u"%s : nom=%s" % (self.__class__.__name__, self['nom'][0])
def __repr__(self): def __repr__(self):
return str(self.__class__.__name__) + " : nom=" + str(self['nom'][0]) return repr(self.__unicode__())
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(proprio, self).__init__(*args, **kwargs) super(proprio, self).__init__(*args, **kwargs)
self._machines = None self._machines = None
self._factures = None self._factures = None
self._factures_last_update = 0
def delete_compte(self, mail): 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 # Je pense qu'en pratique cette vérification ne sert à rien puisqu'on se fera jetter à la tentative de modification
@ -929,7 +948,6 @@ class proprio(CransLdapObject):
return self['uid'][0] return self['uid'][0]
elif login: elif login:
fn = crans_utils.strip_accents(unicode(self['prenom'][0]).capitalize())
ln = crans_utils.strip_accents(unicode(self['nom'][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() login = crans_utils.strip_spaces(crans_utils.strip_accents(login), by=u'-').lower()
if not re.match('^[a-z][-a-z]{1,15}$', login): if not re.match('^[a-z][-a-z]{1,15}$', login):
@ -937,25 +955,43 @@ class proprio(CransLdapObject):
if crans_utils.mailexist(login): if crans_utils.mailexist(login):
raise ValueError("Login existant ou correspondant à un alias mail.") raise ValueError("Login existant ou correspondant à un alias mail.")
home = u'/home/' + login if self.ldap_name == u'adherent':
home = u'/home/' + login[0] + '/' + login
if os.path.exists(home): if os.path.exists(home):
raise ValueError('Création du compte impossible : home existant') raise ValueError('Création du compte impossible : home existant')
if os.path.exists("/var/mail/" + login): if os.path.exists("/home/mail/" + login):
raise ValueError('Création du compte impossible : /var/mail/%s existant' % str(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._modifs['objectClass'] = [u'adherent', u'cransAccount', u'posixAccount', u'shadowAccount']
self['uid' ] = [login]
self['homeDirectory'] = [home] self['homeDirectory'] = [home]
self['cn'] = [fn + u' ' + ln]
self['mail'] = [login + u"@crans.org"] 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): 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['canonicalAlias'] = [calias]
self['cn'] = [ fn + u' ' + ln ]
else:
# C'est un club
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):
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)] self['loginShell'] = [unicode(shell)]
if hash_pass:
self['userPassword'] = [unicode(hash_pass)] self['userPassword'] = [unicode(hash_pass)]
self["solde"] = 0.0
if uidNumber: if uidNumber:
if self.conn.search(u'(uidNumber=%s)' % uidNumber): if self.conn.search(u'(uidNumber=%s)' % uidNumber):
@ -1009,11 +1045,45 @@ class proprio(CransLdapObject):
def fin_adhesion(self): def fin_adhesion(self):
"""Retourne la date de fin d'adhésion""" """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([
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', [])
] + [attributs.finAdhesion.default])
def fin_connexion(self): def fin_connexion(self):
"""Retourne la date de fin de connexion""" """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([
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', [])
] + [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 = (
_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
def paiement_ok(self, no_bl=False): def paiement_ok(self, no_bl=False):
u""" u"""
@ -1022,15 +1092,33 @@ class proprio(CransLdapObject):
""" """
if self.dn == variables.base_dn: if self.dn == variables.base_dn:
return True return True
if not no_bl: if not no_bl:
for bl in self.blacklist_actif(): for bl in self.blacklist_actif():
if bl['type'] == 'paiement': if bl['type'] == 'paiement':
return False return False
_now = crans_utils.localized_datetime()
if isinstance(self, adherent): if isinstance(self, adherent):
fin_paiement = min(self.fin_adhesion(), self.fin_connexion()) fin_paiement = min(self.fin_adhesion(), self.fin_connexion())
else: else:
fin_paiement = self.fin_adhesion() 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 return paiement
def carte_ok(self): def carte_ok(self):
@ -1088,6 +1176,7 @@ class proprio(CransLdapObject):
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: for m in self._factures:
m._proprio = self m._proprio = self
self._factures_last_update = time.time()
# Si on manipule un objet pas encore enregistré dans la la bdd # Si on manipule un objet pas encore enregistré dans la la bdd
except ldap.NO_SUCH_OBJECT: except ldap.NO_SUCH_OBJECT:
self._factures = [] self._factures = []
@ -1147,20 +1236,44 @@ class proprio(CransLdapObject):
class machine(CransLdapObject): class machine(CransLdapObject):
u""" Une machine """ u""" Une machine """
__slots__ = ("_proprio", "_certificats") __slots__ = ("_proprio", "_certificats")
can_be_by = { variables.created: [attributs.nounou, attributs.bureau, attributs.cableur, attributs.parent, attributs.respo], can_be_by = {
variables.modified: [attributs.nounou, attributs.bureau, attributs.cableur, attributs.parent, attributs.respo], variables.created: [
variables.deleted: [attributs.nounou, attributs.bureau, attributs.cableur, attributs.parent, attributs.respo], 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, attribs = [
attributs.mid, attributs.macAddress, attributs.host,
attributs.rid, attributs.info, attributs.blacklist, attributs.hostAlias, attributs.rid, attributs.info, attributs.blacklist, attributs.hostAlias,
attributs.exempt, attributs.portTCPout, attributs.portTCPin, attributs.exempt, attributs.portTCPout, attributs.portTCPin,
attributs.portUDPout, attributs.portUDPin, attributs.sshFingerprint, attributs.portUDPout, attributs.portUDPin, attributs.sshFingerprint,
attributs.ipHostNumber, attributs.ip6HostNumber, attributs.historique, attributs.ipHostNumber, attributs.ip6HostNumber, attributs.historique,
attributs.dnsIpv6, attributs.machineAlias] attributs.dnsIpv6, attributs.machineAlias,
]
def __unicode__(self):
return u"%s : host=%s" % (self.__class__.__name__, self['host'][0])
def __repr__(self): def __repr__(self):
return str(self.__class__.__name__) + " : host=" + str(self['host'][0]) return repr(self.__unicode__())
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(machine, self).__init__(*args, **kwargs) super(machine, self).__init__(*args, **kwargs)
@ -1181,6 +1294,10 @@ class machine(CransLdapObject):
if domain in certificat['hostCert']: 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) 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): def proprio(self, mode=None, refresh=False):
u"""Renvoie le propriétaire de la machine""" u"""Renvoie le propriétaire de la machine"""
if not hasattr(self, '_proprio') or not self._proprio or refresh: if not hasattr(self, '_proprio') or not self._proprio or refresh:
@ -1267,7 +1384,7 @@ class machine(CransLdapObject):
sbm['rid'] = (orid, nrid) sbm['rid'] = (orid, nrid)
# Les macAddress sont déjà des unicodes. # Les macAddress sont déjà des unicodes.
# On change l'ip6 # 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'])) nip6 = unicode(crans_utils.ip6_of_mac(new['macAddress'], new['rid']))
try: try:
oip6 = unicode(self._modifs['ip6HostNumber'][0]) oip6 = unicode(self._modifs['ip6HostNumber'][0])
@ -1301,6 +1418,7 @@ class machine(CransLdapObject):
if unicode(self['ipHostNumber'][0]) != unicode(crans_utils.ip4_of_rid(sbm['rid'][1])): if unicode(self['ipHostNumber'][0]) != unicode(crans_utils.ip4_of_rid(sbm['rid'][1])):
raise ValueError("L'ipv4 et le rid ne concordent pas !") 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['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']:
if sbm['ipHostNumber'][1] == u"": if sbm['ipHostNumber'][1] == u"":
ip4 = [] ip4 = []
@ -1337,14 +1455,21 @@ class AssociationCrans(proprio):
def delete(self, comm, login): def delete(self, comm, login):
pass pass
def __unicode__(self):
return u"Le Crans"
def __repr__(self): def __repr__(self):
return str(self.__class__.__name__) + " : Le Crans" return repr(self.__unicode__())
class BaseInvites(proprio): class BaseInvites(proprio):
u"""Un artefact de la base ldap""" u"""Un artefact de la base ldap"""
__slots__ = () __slots__ = ()
def __unicode__(self):
return u"%s" % (self.__class__.__name__,)
def __repr__(self): def __repr__(self):
return str(self.__class__.__name__) return repr(self.__unicode__())
def delete(self, comm, login): def delete(self, comm, login):
raise EnvironmentError("Les pauvres invites") raise EnvironmentError("Les pauvres invites")
@ -1356,7 +1481,8 @@ class adherent(proprio):
@property @property
def attribs(self): def attribs(self):
return super(adherent, self).attribs + [attributs.aid, attributs.prenom, attributs.tel, return super(adherent, self).attribs + [
attributs.aid, attributs.prenom, attributs.tel,
attributs.mail, attributs.mailInvalide, attributs.charteMA, attributs.mail, attributs.mailInvalide, attributs.charteMA,
attributs.derniereConnexion, attributs.gpgFingerprint, attributs.derniereConnexion, attributs.gpgFingerprint,
attributs.carteEtudiant, attributs.etudes, attributs.carteEtudiant, attributs.etudes,
@ -1364,8 +1490,11 @@ class adherent(proprio):
] ]
ldap_name = "adherent" ldap_name = "adherent"
def __unicode__(self):
return u"Adhérent : %s %s" % (self['prenom'][0], self['nom'][0])
def __repr__(self): def __repr__(self):
return "Adhérent : " + str(self['prenom'][0]) + " " + str(self['nom'][0]) return repr(self.__unicode__())
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(adherent, self).__init__(*args, **kwargs) super(adherent, self).__init__(*args, **kwargs)
@ -1391,13 +1520,31 @@ 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)) 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) super(adherent, self).delete(comm, login)
def oid(self):
"""Retourne l'id de l'objet courant"""
return self['aid']
@crans_object @crans_object
class club(proprio): class club(proprio):
u"""Club crans""" u"""Club crans"""
__slots__ = () __slots__ = ()
can_be_by = { variables.created: [attributs.nounou, attributs.bureau, attributs.cableur], can_be_by = {
variables.modified: [attributs.nounou, attributs.bureau, attributs.respo, attributs.cableur, attributs.soi], variables.created: [
variables.deleted: [attributs.nounou, attributs.bureau], 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" ldap_name = "club"
@ -1408,8 +1555,15 @@ class club(proprio):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(club, self).__init__(*args, **kwargs) super(club, self).__init__(*args, **kwargs)
def __unicode__(self):
return u"Club : %s" % (self['nom'][0],)
def __repr__(self): def __repr__(self):
return "Club : " + str(self['nom'][0]) return repr(self.__unicode__())
def oid(self):
"""Retourne l'id de l'objet courant"""
return self['cid']
@crans_object @crans_object
class machineFixe(machine): class machineFixe(machine):
@ -1459,50 +1613,95 @@ class machineWifi(machine):
@crans_object @crans_object
class machineCrans(machine): class machineCrans(machine):
__slots__ = () __slots__ = ()
can_be_by = { variables.created: [attributs.nounou], can_be_by = {
variables.modified: [attributs.nounou], variables.created: [
variables.deleted: [attributs.nounou], 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" ldap_name = "machineCrans"
@crans_object @crans_object
class borneWifi(machine): class borneWifi(machine):
__slots__ = () __slots__ = ()
can_be_by = { variables.created: [attributs.nounou], can_be_by = {
variables.modified: [attributs.nounou], variables.created: [
variables.deleted: [attributs.nounou], attributs.nounou,
],
variables.modified: [
attributs.nounou,
],
variables.deleted: [
attributs.nounou,
],
} }
attribs = machine.attribs + [attributs.canal, attributs.puissance, attributs.hotspot, attribs = machine.attribs + [
attributs.prise, attributs.positionBorne, attributs.nvram] attributs.canal, attributs.puissance, attributs.hotspot,
attributs.prise, attributs.positionBorne, attributs.nvram,
]
ldap_name = "borneWifi" ldap_name = "borneWifi"
@crans_object @crans_object
class switchCrans(machine): class switchCrans(machine):
__slots__ = () __slots__ = ()
can_be_by = { variables.created: [attributs.nounou], can_be_by = {
variables.modified: [attributs.nounou], variables.created: [
variables.deleted: [attributs.nounou], attributs.nounou,
],
variables.modified: [
attributs.nounou,
],
variables.deleted: [
attributs.nounou,
],
} }
attribs = machine.attribs + [attributs.nombrePrises] attribs = machine.attribs + [
attributs.nombrePrises,
]
ldap_name = "switchCrans" ldap_name = "switchCrans"
@crans_object @crans_object
class facture(CransLdapObject): class facture(CransLdapObject):
__slots__ = ("_proprio", "_recuPaiement") __slots__ = ("_proprio", "_recuPaiement")
can_be_by = { variables.created: [attributs.nounou, attributs.bureau, attributs.cableur], can_be_by = {
variables.modified: [attributs.nounou, attributs.bureau, attributs.cableur], variables.created: [
variables.deleted: [attributs.nounou, attributs.bureau, attributs.cableur], 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, attribs = [
attributs.fid, attributs.modePaiement, attributs.recuPaiement,
attributs.historique, attributs.article, attributs.info, attributs.historique, attributs.article, attributs.info,
attributs.debutAdhesion, attributs.finAdhesion, attributs.debutConnexion, attributs.debutAdhesion, attributs.finAdhesion, attributs.debutConnexion,
attributs.finConnexion, attributs.controle ] attributs.finConnexion, attributs.controle,
]
ldap_name = "facture" ldap_name = "facture"
def __unicode__(self):
return u"Facture : fid=%s" % (self['fid'][0],)
def __repr__(self): def __repr__(self):
return str(self.__class__.__name__) + " : fid=" + str(self['fid'][0]) return repr(self.__unicode__())
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self._proprio = None self._proprio = None
@ -1514,6 +1713,10 @@ class facture(CransLdapObject):
raise EnvironmentError("Paiement déjà effectué pour cette facture, impossible de modifier son contenu") raise EnvironmentError("Paiement déjà effectué pour cette facture, impossible de modifier son contenu")
return super(facture, self).__setitem__(attr, value) return super(facture, self).__setitem__(attr, value)
def oid(self):
"""Retourne l'id de l'objet courant"""
return self['fid']
def total(self): def total(self):
total = 0 total = 0
for article in self["article"]: for article in self["article"]:
@ -1539,11 +1742,12 @@ class facture(CransLdapObject):
proprio_save = True proprio_save = True
return proprio_save return proprio_save
if not self._recuPaiement: if not self._recuPaiement:
with self.proprio() as proprio: with self.proprio() as proprio:
proprio_save = credite_arts(proprio) proprio_save = credite_arts(proprio)
# On vient de créditer, le paiement a été reçu # 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.datetime_to_generalized_time_format(crans_utils.localized_datetime())
self._recuPaiement = True self._recuPaiement = True
# Il faudrait faire quelquechose pour que si l'enregistrement suivant de la facture crash, # Il faudrait faire quelquechose pour que si l'enregistrement suivant de la facture crash,
@ -1563,6 +1767,30 @@ class facture(CransLdapObject):
proprio.save() proprio.save()
raise 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 - 1 ) % 12 + 1)
return
# On effectue les opérations
do_adh_conn(self.proprio())
self.crediter()
def proprio(self, refresh=False): def proprio(self, refresh=False):
u"""Renvoie le propriétaire de la facture""" u"""Renvoie le propriétaire de la facture"""
if refresh or not self._proprio: if refresh or not self._proprio:
@ -1572,19 +1800,41 @@ class facture(CransLdapObject):
@crans_object @crans_object
class baseCert(CransLdapObject): class baseCert(CransLdapObject):
__slots__ = ("_machine",) __slots__ = ("_machine",)
can_be_by = { variables.created: [attributs.nounou, attributs.bureau, attributs.parent], can_be_by = {
variables.modified: [attributs.nounou, attributs.bureau, attributs.parent], variables.created: [
variables.deleted: [attributs.nounou, attributs.bureau, attributs.parent], 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, default_attribs = [
attributs.info, attributs.csr ] attributs.xid, attributs.certificat, attributs.hostCert, attributs.historique,
attributs.info, attributs.csr,
]
tlsa_attribs = [ attributs.certificatUsage, attributs.selector, attributs.matchingType, tlsa_attribs = [
attributs.portTCPin, attributs.portUDPin] attributs.certificatUsage, attributs.selector, attributs.matchingType,
x509_attribs = [ attributs.issuerCN, attributs.start, attributs.end, attributs.portTCPin, attributs.portUDPin,
attributs.crlUrl, attributs.revocked, attributs.serialNumber ] ]
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 @property
def attribs(self): def attribs(self):

View file

@ -3,13 +3,17 @@
{{["aid=",o.aid.0]|join|coul('bleu')}} {{"Nom : "|coul('gras')}}{{o.prenom|join(' ')}} {{o.nom|join(' ')}} {{["aid=",o.aid.0]|join|coul('bleu')}} {{"Nom : "|coul('gras')}}{{o.prenom|join(' ')}} {{o.nom|join(' ')}}
{% endblock%} {% endblock%}
{% block proprio %} {% block proprio %}
{% if disp_telephone %}
{{"Numéro de téléphone : "|coul('gras')}}{{o.tel|telephone|join(', ')}} {{"Numéro de téléphone : "|coul('gras')}}{{o.tel|telephone|join(', ')}}
{% endif %}
{% if disp_adresse %}
{% if o.chbre.0 == 'EXT' and o.postalAddress %} {% if o.chbre.0 == 'EXT' and o.postalAddress %}
{{"Adresse : "|coul('gras')}}{{o.postalAddress.0}} {{o.postalAddress.1}} {{"Adresse : "|coul('gras')}}{{o.postalAddress.0}} {{o.postalAddress.1}}
{{o.postalAddress.2}} {{o.postalAddress.3}} {{o.postalAddress.2}} {{o.postalAddress.3}}
{% else %} {% else %}
{{"Chambre : "|coul('gras')}}{{o.chbre.0}} ({{o.chbre.0|string|prise_etat}}) {{"Chambre : "|coul('gras')}}{{o.chbre.0}} ({{o.chbre.0|string|prise_etat}})
{% endif %} {% endif %}
{% endif %}
{{"Études : "|coul('gras')}}{{o.etudes|join(' ')}} {{"Études : "|coul('gras')}}{{o.etudes|join(' ')}}
{{adh}} {% if o.get('controle', []) and 'p' in o.controle.0.value %}{{"(OK)"|coul('vert')}}{% endif %} {{adh}} {% if o.get('controle', []) and 'p' in o.controle.0.value %}{{"(OK)"|coul('vert')}}{% endif %}

View file

@ -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 %} {% if o.article %}
{{"Article : "|coul('gras')}} Prix N° Total Commentaire {{"Article : "|coul('gras')}} Prix N° Total Commentaire
{% set total = [] %}{% for a in o.article %} {% set total = [] %}{% for a in o.article %}

View file

@ -6,6 +6,9 @@
{{"IPv4 : "|coul('gras')}}{{o.ipHostNumber|join(', ')}} {{"IPv4 : "|coul('gras')}}{{o.ipHostNumber|join(', ')}}
{% if o.ip6HostNumber %}{{"IPv6 : "|coul('gras')}}{{o.ip6HostNumber|join(', ')}} {% if o.ip6HostNumber %}{{"IPv6 : "|coul('gras')}}{{o.ip6HostNumber|join(', ')}}
{% endif %} {% 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 %} {{"DnsIpv6 : "|coul('gras')}}{% if not o.dnsIpv6 or o.dnsIpv6.0.value %}
{{"TRUE"|coul('vert')}} {{"TRUE"|coul('vert')}}
{% else %} {% else %}

View file

@ -16,6 +16,7 @@ import gestion.annuaires_pg
import gestion.hptools2 as hptools2 import gestion.hptools2 as hptools2
import lc_ldap.attributs as attributs import lc_ldap.attributs as attributs
import lc_ldap.crans_utils as crans_utils
def try_import(lib): def try_import(lib):
""" """
@ -201,16 +202,21 @@ def list_factures(factures, width=None):
data = [] data = []
for facture in factures: for facture in factures:
controle = facture.get('controle', [""])[0] controle = facture.get('controle', [""])[0]
if controle == "TRUE": if controle == u"TRUE":
controle = style(u"Validée", "vert") controle = style(u"Validée", "vert")
elif controle == "FALSE": elif controle == u"FALSE":
controle = style(u"Invalide", "rouge") controle = style(u"Invalide", "rouge")
else: else:
controle = u"N/A" 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([ data.append([
facture['fid'][0], facture['fid'][0],
facture['modePaiement'][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, controle,
' '.join(attr['code'] for attr in facture.get('article',[])), ' '.join(attr['code'] for attr in facture.get('article',[])),
u"%s €" % sum([float(a['pu'])*int(a['nombre']) for a in facture.get('article',[])]) u"%s €" % sum([float(a['pu'])*int(a['nombre']) for a in facture.get('article',[])])
@ -223,49 +229,69 @@ def list_factures(factures, width=None):
width=width) width=width)
def list_adherents(adherents, width=None): def list_adherents(adherents, width=None):
return tableau([ return tableau(
[a['aid'][0], [
[
a['aid'][0],
u' '.join(unicode(i) for i in a['prenom'] + a['nom']), 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'), 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()) 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'], for a in adherents
largeur = [5, 35, 5, 1, '*'], ],
alignement = ['d', 'c', 'c', 'c', 'g'], titre=[u'aid', u'Prénom Nom', u'Chbre', u'A', u'C', u'Machines'],
width=width) largeur=[5, 35, 5, 1, 1, '*'],
alignement=['d', 'c', 'c', 'c', 'c', 'g'],
width=width
)
def list_clubs(clubs, width=None): def list_clubs(clubs, width=None):
return tableau([ return tableau(
[a['cid'][0], [
[
a['cid'][0],
u' '.join(unicode(i) for i in a['nom']), u' '.join(unicode(i) for i in a['nom']),
a['chbre'][0], style('o', 'vert') if a.paiement_ok() else style('n', 'rouge'), 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()) 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'], for a in clubs
largeur = [5, 35, 5, 1, '*'], ],
alignement = ['d', 'c', 'c', 'c', 'g'], titre = [u'cid', u'Nom', u'Chbre', u'A', u'C', u'Machines'],
width=width) largeur = [5, 35, 5, 1, 1, '*'],
alignement = ['d', 'c', 'c', 'c', 'c', 'g'],
width=width
)
def proprio(proprio, params): def proprio(proprio, params):
_now = crans_utils.localized_datetime()
params['o'] = proprio params['o'] = proprio
etat_administratif = [] etat_administratif = []
if proprio.paiement_ok(): if proprio.paiement_ok():
etat_administratif.append(style(u"à jour", "vert")) etat_administratif.append(style(u"à jour", "vert"))
if not proprio.paiement_ok(): if not proprio.paiement_ok():
etat_administratif.append(style(u"cotisation non réglée", "violet")) 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") 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(): elif proprio.paiement_ok():
adh = style(u"Adhésion terminée, mais il y a un sursis.", 'orange') adh = style(u"Adhésion terminée, mais il y a un sursis.", 'orange')
else: else:
adh = style(u"Pas adhérent actuellement.", 'rouge') adh = style(u"Pas adhérent actuellement.", 'rouge')
params["adh"] = adh 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") 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(): elif proprio.paiement_ok():
conn = style(u"Connexion terminée, mais il y a un sursis.", 'orange') conn = style(u"Connexion terminée, mais il y a un sursis.", 'orange')
else: else:
conn = style(u"Pas connecté actuellement.", 'rouge') conn = style(u"Pas connecté actuellement.", 'rouge')
params["conn"] = conn params["conn"] = conn
params['etat_administratif'] = etat_administratif params['etat_administratif'] = etat_administratif
@ -298,7 +324,10 @@ def blacklist(blacklist, params):
def sprint(object, historique=5, blacklist_len=5, **params): def sprint(object, historique=5, blacklist_len=5, **params):
from lc_ldap import objets, attributs 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): if isinstance(object, objets.machine):
return machine(object, params) return machine(object, params)
elif isinstance(object, objets.adherent): elif isinstance(object, objets.adherent):

View file

@ -32,7 +32,6 @@ services_to_attrs['mail_modif'] = [ attributs.droits, attributs.exempt ] + servi
services_to_objects={} services_to_objects={}
services_to_objects['delete']={} services_to_objects['delete']={}
services_to_objects['create']={} services_to_objects['create']={}
services_to_objects['create']['home'] = [objets.adherent, objets.club]
services_to_objects['delete']['del_user'] = [objets.adherent, objets.club] services_to_objects['delete']['del_user'] = [objets.adherent, objets.club]
NOW = time.time() NOW = time.time()

View file

@ -15,7 +15,7 @@ try:
from gestion import secrets_new as secrets from gestion import secrets_new as secrets
except ImportError: except ImportError:
sys.stderr.write("lc_ldap shortcuts: shaa, cannot import secrets_new. " + 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', []))) "(argv: %s)\n" % " ".join(getattr(sys, 'argv', [])))
sys.path.append("/usr/scripts") sys.path.append("/usr/scripts")
from gestion import secrets_new as secrets from gestion import secrets_new as secrets
@ -51,7 +51,7 @@ def lc_ldap_test(*args, **kwargs):
except OSError: except OSError:
pass pass
# On impose le serveur # 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') kwargs.setdefault("dn", 'cn=admin,dc=crans,dc=org')
# Le mot de passe de la base de test # Le mot de passe de la base de test
kwargs.setdefault("cred", variables.ldap_test_password) kwargs.setdefault("cred", variables.ldap_test_password)

View file

@ -14,8 +14,6 @@ import lc_ldap
import shortcuts import shortcuts
import variables import variables
## import dans /usr/scripts/
sys.path.append("/usr/scripts/")
from gestion.affich_tools import anim, OK, cprint, ERREUR from gestion.affich_tools import anim, OK, cprint, ERREUR
mail_format = False mail_format = False