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
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:
@ -242,7 +243,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):
@ -350,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):
@ -393,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
@ -513,14 +525,14 @@ class aid(intAttr):
self.value = int(aid)
@crans_attribute
class uid(Attr):
class uid(rightProtectedAttr):
__slots__ = ()
singlevalue = True
option = False
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
@ -636,25 +648,31 @@ class generalizedTimeFormat(Attr):
une donnée de temps suivant la RFC 4517
"""
__slots__ = ("_stamp",)
default = "19700101000000Z"
__slots__ = ("datetime",)
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.value == othertime
else:
return False
@ -663,14 +681,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.value < othertime
else:
return False
@ -685,15 +705,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))
else:
self._stamp = from_generalized_time_format(gtf)
self.value = datetime_from_generalized_time_format(gtf)
elif isinstance(gtf, datetime.datetime):
self.value = gtf
elif isinstance(gtf, float):
self._stamp = gtf
self.value = to_generalized_time_format(gtf)
@crans_attribute
class debutAdhesion(generalizedTimeFormat):
@ -740,28 +754,43 @@ 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']:
mail, end = str(self).split('@', 1)
if end.startswith('crans'):
# On initialise le résultat, s'il vaut true en fin de course, le mail
# 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:
smtp = smtplib.SMTP(smtpserv)
smtp.putcmd("vrfy", mail)
res = smtp.getreply()[0] in [250, 252]
smtp.close()
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:
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):
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
@ -787,7 +816,6 @@ class mailAlias(mail):
optional = True
unique = True
legend = u"Alias mail"
can_modify = [soi, cableur, nounou]
category = 'mail'
ldap_name = "mailAlias"
@ -807,8 +835,7 @@ class mailExt(mail):
singlevalue = False
optional = True
unique = True
legend = u"Mail de secours"
can_modify = [soi, cableur, nounou]
legend = u"Mail de redirection ou de secours"
category = 'mail'
ldap_name = "mailExt"
@ -863,7 +890,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)
@ -1055,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):
@ -1087,7 +1113,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'
@ -1106,8 +1132,8 @@ class puissance(Attr):
optional = True
legend = u"puissance d'émission pour les bornes wifi"
category = 'wifi'
can_modify = [nounou]
ldap_name = "puissance"
default = '60'
@crans_attribute
class canal(intAttr):
@ -1116,8 +1142,8 @@ class canal(intAttr):
optional = True
legend = u'Canal d\'émission de la borne'
category = 'wifi'
can_modify = [nounou]
ldap_name = "canal"
default = "11"
@crans_attribute
class hotspot(boolAttr):
@ -1126,25 +1152,24 @@ class hotspot(boolAttr):
optional = True
legend = u'Hotspot'
category = 'wifi'
can_modify = [nounou]
ldap_name = "hotspot"
default = "FALSE"
@crans_attribute
class positionBorne(Attr):
__slots__ = ()
legend = u"Position de la borne"
category = "wifi"
can_modify = [nounou]
singlevalue = True
optional = True
ldap_name = "positionBorne"
default = "0 0"
@crans_attribute
class nvram(Attr):
__slots__ = ()
legend = u"Configuration speciale"
optional = True
can_modify = [nounou]
ldap_name = "nvram"
class portAttr(Attr):
@ -1153,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:
@ -1227,7 +1251,6 @@ class nombrePrises(intAttr):
singlevalue = True
optional = True
categoriy = 'base_tech'
can_modify = [nounou]
ldap_name = "nombrePrises"
@crans_attribute
@ -1237,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
@ -1347,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
@ -1460,7 +1482,7 @@ class homeDirectory(rightProtectedAttr):
ldap_name = "homeDirectory"
@crans_attribute
class loginShell(Attr):
class loginShell(rightProtectedAttr):
__slots__ = ()
singlevalue = True
optional = True
@ -1477,7 +1499,7 @@ class loginShell(Attr):
self.value = shell
@crans_attribute
class uidNumber(intAttr):
class uidNumber(intAttr, rightProtectedAttr):
__slots__ = ()
singlevalue = True
optional = True
@ -1485,15 +1507,17 @@ 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):
class gidNumber(intAttr, rightProtectedAttr):
__slots__ = ()
singlevalue = True
optional = True
legend = "Le gid du compte de l'adhérent"
category = 'id'
ldap_name = "gidNumber"
can_modify = [cableur, nounou]
@crans_attribute
class gecos(Attr):
@ -1503,6 +1527,7 @@ class gecos(Attr):
legend = "Le gecos"
category = 'id'
ldap_name = "gecos"
can_modify = [cableur, nounou]
@crans_attribute
class userPassword(rightProtectedAttr):
@ -1595,6 +1620,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)
@ -1607,6 +1633,7 @@ class cn(Attr):
__slots__ = ()
singlevalue = True
optional = False
can_modify = [cableur, nounou]
category = 'id'
ldap_name = "cn"
@ -1669,7 +1696,7 @@ class modePaiement(Attr):
self.value = mode
@crans_attribute
class recuPaiement(Attr):
class recuPaiement(generalizedTimeFormat):
__slots__ = ()
ldap_name = "recuPaiement"
can_modify = [cableur, nounou]
@ -1739,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
@ -1747,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
@ -1778,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

View file

@ -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:
@ -55,6 +54,11 @@ try:
except:
pytz = None
try:
import dateutil.tz
except:
dateutil = None
DEVNULL = open(os.devnull, 'w')
def find_rid_plage(rid):
@ -245,7 +249,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")
@ -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"))
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"""
"""
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:
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"""

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# LC_LDAP.PY-- LightWeight CransLdap
@ -39,6 +38,7 @@
import os
import sys
import re
import time
from contextlib import contextmanager
import ldap
@ -53,11 +53,12 @@ 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
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
@ -112,7 +117,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
@ -247,6 +252,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
@ -256,14 +262,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'):
@ -311,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)

View file

@ -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):
"""
@ -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()

446
objets.py
View file

@ -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
@ -66,6 +66,7 @@ import cranslib.deprecated
#: Champs à ignorer dans l'historique
HIST_IGNORE_FIELDS = ["modifiersName", "entryCSN", "modifyTimestamp", "historique"]
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 à
@ -100,7 +101,8 @@ 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],
can_be_by = {
variables.created: [attributs.nounou],
variables.modified: [attributs.nounou],
variables.deleted: [attributs.nounou],
}
@ -205,18 +207,9 @@ 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']
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")
@ -228,8 +221,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)
@ -295,8 +286,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.
@ -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"
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:])
@ -343,8 +335,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
@ -353,7 +345,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)
@ -362,7 +354,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)
@ -370,9 +362,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))
@ -453,7 +445,7 @@ class CransLdapObject(object):
ldif = self._modifs.to_ldif()
for attr in binary:
ldif['%s;binary' % attr] = ldif[attr]
del(ldif[attr])
del ldif[attr]
# Création de la requête LDAP
modlist = addModlist(ldif)
# Requête LDAP de création de l'objet
@ -592,11 +584,11 @@ 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, [])
del(ldif[attr])
ldif['%s;binary' % (attr,)] = ldif[attr]
orig_ldif['%s;binary' % (attr,)] = orig_ldif.get(attr, [])
del ldif[attr]
try:
del(orig_ldif[attr])
del orig_ldif[attr]
except KeyError:
pass
@ -678,8 +670,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:
@ -847,31 +842,51 @@ 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 repr(self.__unicode__())
pass
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,],
__slots__ = ("_machines", "_factures", "full", '_factures_last_update')
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,
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.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]
attributs.finConnexion,
]
@property
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)"""
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 repr(self.__unicode__())
def __init__(self, *args, **kwargs):
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
@ -929,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):
@ -937,25 +955,43 @@ class proprio(CransLdapObject):
if crans_utils.mailexist(login):
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):
raise ValueError('Création du compte impossible : home existant')
if os.path.exists("/var/mail/" + login):
raise ValueError('Création du compte impossible : /var/mail/%s existant' % str(login))
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['uid' ] = [login]
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):
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 ]
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)]
if hash_pass:
self['userPassword'] = [unicode(hash_pass)]
self["solde"] = 0.0
if uidNumber:
if self.conn.search(u'(uidNumber=%s)' % uidNumber):
@ -1009,11 +1045,45 @@ 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([
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):
"""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):
u"""
@ -1022,15 +1092,33 @@ 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
_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
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)
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 = []
@ -1147,20 +1236,44 @@ 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,
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]
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 repr(self.__unicode__())
def __init__(self, *args, **kwargs):
super(machine, self).__init__(*args, **kwargs)
@ -1181,6 +1294,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:
@ -1267,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])
@ -1301,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 = []
@ -1337,14 +1455,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 repr(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 repr(self.__unicode__())
def delete(self, comm, login):
raise EnvironmentError("Les pauvres invites")
@ -1356,7 +1481,8 @@ class adherent(proprio):
@property
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.derniereConnexion, attributs.gpgFingerprint,
attributs.carteEtudiant, attributs.etudes,
@ -1364,8 +1490,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 repr(self.__unicode__())
def __init__(self, *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))
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"""
__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"
@ -1408,8 +1555,15 @@ 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 repr(self.__unicode__())
def oid(self):
"""Retourne l'id de l'objet courant"""
return self['cid']
@crans_object
class machineFixe(machine):
@ -1459,50 +1613,95 @@ 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,
attribs = [
attributs.fid, attributs.modePaiement, attributs.recuPaiement,
attributs.historique, attributs.article, attributs.info,
attributs.debutAdhesion, attributs.finAdhesion, attributs.debutConnexion,
attributs.finConnexion, attributs.controle ]
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 repr(self.__unicode__())
def __init__(self, *args, **kwargs):
self._proprio = None
@ -1514,6 +1713,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"]:
@ -1539,11 +1742,12 @@ class facture(CransLdapObject):
proprio_save = True
return proprio_save
if not self._recuPaiement:
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.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,
@ -1563,6 +1767,30 @@ 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 - 1 ) % 12 + 1)
return
# On effectue les opérations
do_adh_conn(self.proprio())
self.crediter()
def proprio(self, refresh=False):
u"""Renvoie le propriétaire de la facture"""
if refresh or not self._proprio:
@ -1572,19 +1800,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):

View file

@ -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 %}

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

View file

@ -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 %}

View file

@ -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',[])])
@ -223,49 +229,69 @@ def list_factures(factures, width=None):
width=width)
def list_adherents(adherents, width=None):
return tableau([
[a['aid'][0],
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'),
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'P', u'Machines'],
largeur = [5, 35, 5, 1, '*'],
alignement = ['d', 'c', 'c', 'c', 'g'],
width=width)
]
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],
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'),
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'P', u'Machines'],
largeur = [5, 35, 5, 1, '*'],
alignement = ['d', 'c', 'c', 'c', 'g'],
width=width)
]
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):
_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
@ -298,7 +324,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):

View file

@ -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()

View file

@ -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
@ -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)

View file

@ -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