Compare commits

..

No commits in common. "master" and "peb_dev" have entirely different histories.

217 changed files with 22913 additions and 25578 deletions

11
.gitignore vendored
View file

@ -17,8 +17,17 @@
# Cr@ns specific ignore files # # Cr@ns specific ignore files #
############################### ###############################
# On ne versionne pas les fiches de déconnexion
surveillance/fiche_deconnexion/*
# Mais on garde de quoi les générer
!/surveillance/fiche_deconnexion/deconnexion_p2p.tex
!/surveillance/fiche_deconnexion/deconnexion_upload.tex
!/surveillance/fiche_deconnexion/generate.py
!/surveillance/fiche_deconnexion/logo.eps
!/surveillance/fiche_deconnexion/logo.eps.old
# Les clés wifi privées # Les clés wifi privées
archive/gestion/clef-wifi* gestion/clef-wifi*
# Autres dépôts git # Autres dépôts git
gestion/logreader/ gestion/logreader/

View file

@ -1,31 +0,0 @@
## Sous-dépôts
À cloner pour faire marcher certains scripts
* `./lc_ldap`
* `./wifi_new`
## Paquets Debian
Rajoutez-en si vous vous rendez compte qu'il en manque, à l'occasion.
* python-ldap
* python-netifaces
* python-psycopg2
* python-netsnmp
* python-pyparsing
* python-markdown
* python-jinja2
* python-beautifulsoup
* python-ipaddr
* python-passlib
* python-dateutil
* python-tz
* python-netaddr
## À faire
* Expliquer l'environnement de test
* tunnel pour apprentis
* http://stackoverflow.com/questions/8021/allow-user-to-set-up-an-ssh-tunnel-but-nothing-else
* snmp et les mibs ! !!

View file

@ -1,8 +1,7 @@
#!/bin/bash /usr/scripts/python.sh #! /usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) Stéphane Glondu, Alexandre Bos, Michel Blockelet # Copyright (C) Stéphane Glondu, Alexandre Bos, Michel Blockelet
# Remanié en 2015 par Gabriel Détraz
# Licence : GPLv2 # Licence : GPLv2
u"""Ce script permet au secrétaire de repérer plus facilement les membres u"""Ce script permet au secrétaire de repérer plus facilement les membres
@ -22,23 +21,22 @@ Les commandes sont :
import sys, os, re import sys, os, re
sys.path.append('/usr/scripts/gestion')
import config
import config.mails
from email_tools import send_email, parse_mail_template
# Fonctions d'affichage # Fonctions d'affichage
from gestion.affich_tools import coul, tableau, prompt, cprint from affich_tools import coul, tableau, prompt, cprint
from utils.sendmail import actually_sendmail
from gestion import mail
# Importation de la base de données # Importation de la base de données
from lc_ldap import shortcuts from ldap_crans import crans_ldap, ann_scol
db = crans_ldap()
# Lors des tests, on m'envoie tous les mails ! # Lors des tests, on m'envoie tous les mails !
from socket import gethostname from socket import gethostname
debug = False debug = False
# Conn à la db
ldap = shortcuts.lc_ldap_admin()
if __name__ == '__main__': if __name__ == '__main__':
if len(sys.argv) > 3 and sys.argv[-2] == '--debug': if len(sys.argv) > 3 and sys.argv[-2] == '--debug':
debug = sys.argv[-1] debug = sys.argv[-1]
@ -67,28 +65,33 @@ def _controle_interactif_adherents(liste):
nb = 0 nb = 0
for a in liste: for a in liste:
valeur = a['charteMA'] valeur = a.charteMA()
if valeur: if valeur:
suggestion = 'o' suggestion = 'o'
else: else:
suggestion = 'n' suggestion = 'n'
ok = prompt(u'[%3d] %s, %s (%s) ?' ok = prompt(u'[%3d] %s, %s (%s) ?'
% (restant, a['nom'][0], a['prenom'][0], a['aid'][0]), suggestion, '').lower() % (restant, a.nom(), a.prenom(), a.id()), suggestion, '').lower()
restant -= 1 restant -= 1
if ok == 'o': if ok == 'o':
nb += 1 nb += 1
if a['charteMA'] != True : if a.charteMA() == False :
modifiable = ldap.search(u'aid=%s' % a['aid'][0], mode='rw') modifiable = db.search('aid=%s' % a.id(), 'w')['adherent'][0]
try: if modifiable._modifiable:
with modifiable[0] as adh: modifiable.charteMA(True)
adh['charteMA']=True cprint(modifiable.save())
adh.history_gen() else:
adh.save() cprint(u'Adhérent %s locké, réessayer plus tard' % modifiable.Nom(), 'rouge')
cprint(u'Controle OK') elif ok == 'n':
except: if a.charteMA() == True:
cprint(u'Adhérent %s locké, réessayer plus tard' % a['nom'][0], 'rouge') modifiable = db.search('aid=%s' % a.id(), 'w')['adherent'][0]
elif ok != 'n': if modifiable._modifiable:
cprint(u'Arrêt du contrôle des membres actifs', 'rouge') modifiable.charteMA(False)
cprint(modifiable.save())
else:
cprint(u'Adhérent %s locké, réessayer plus tard' % modifiable.Nom(), 'rouge')
else:
cprint(u'Arrêt du contrôle %s des membres actifs' % explicite, 'rouge')
break break
return nb, len(liste)-nb return nb, len(liste)-nb
@ -96,12 +99,12 @@ def _controle_interactif_adherents(liste):
def liste_charte_nok(): def liste_charte_nok():
"""Retourne la liste des membres actifs qui n'ont pas signé la charte.""" """Retourne la liste des membres actifs qui n'ont pas signé la charte."""
liste_actifs = ldap.search(u'droits=*') liste_actifs = db.search('droits=*')['adherent']
liste_nok = [] liste_nok = []
for adh in liste_actifs: for adh in liste_actifs:
if (len([droit for droit in adh['droits'] if (len([droit for droit in adh.droits()
if droit not in ['Multimachines', 'Webradio']]) > 0 if droit not in ['Multimachines', 'Webradio']]) > 0
and not adh['charteMA']): and not adh.charteMA()):
liste_nok.append(adh) liste_nok.append(adh)
return liste_nok return liste_nok
@ -113,7 +116,7 @@ def controle_interactif():
# Tri de la liste des adhérents selon nom, prénom # Tri de la liste des adhérents selon nom, prénom
# Ça peut se faire plus facilement en Python 2.4 avec l'argument key # Ça peut se faire plus facilement en Python 2.4 avec l'argument key
todo_list.sort(lambda x, y: cmp((x['nom'][0], x['prenom'][0]), (y['nom'][0], y['prenom'][0]))) todo_list.sort(lambda x, y: cmp((x.nom(), x.prenom()), (y.nom(), y.prenom())))
# Zou ! # Zou !
ok, nok = _controle_interactif_adherents(todo_list) ok, nok = _controle_interactif_adherents(todo_list)
@ -129,18 +132,19 @@ def spammer():
todo_list = liste_charte_nok() todo_list = liste_charte_nok()
if todo_list: if todo_list:
from smtplib import SMTP
connexion = SMTP()
if gethostname().split(".")[0] == 'redisdead':
connexion.connect("localhost")
else: connexion.connect("redisdead.crans.org")
print "Envoi des mails de rappel pour les chartes des membres actifs" print "Envoi des mails de rappel pour les chartes des membres actifs"
for adh in todo_list: for adh in todo_list:
to = adh['mail'][0] to = adh.email()
print to print to
if not debug: if not debug:
From = u"ca@crans.org" data = config.mails.txt_charte_MA % {'From' : u"ca@crans.org", 'To' : to}
data=mail.generate('missing_charte_MA', { connexion.sendmail("ca@crans.org",to,data.encode('utf-8'))
'To': unicode(to),
'From': From,
})
actually_sendmail(u'ca@crans.org', (unicode(to),), data)
def __usage(message=None): def __usage(message=None):
""" Comment ça marche ? """ """ Comment ça marche ? """
@ -159,7 +163,7 @@ if __name__ == '__main__' :
__usage(u'Mauvaise utilisation de liste') __usage(u'Mauvaise utilisation de liste')
print "Liste des membres actifs n'ayant pas signé la charte :" print "Liste des membres actifs n'ayant pas signé la charte :"
for adh in liste_charte_nok(): for adh in liste_charte_nok():
print unicode(adh['prenom'][0]) + u" " + unicode(adh['nom'][0]) print adh.Nom()
elif sys.argv[1] == 'modif': elif sys.argv[1] == 'modif':
if len(sys.argv) != 2: if len(sys.argv) != 2:
__usage(u'Mauvaise utilisation de modif') __usage(u'Mauvaise utilisation de modif')

View file

@ -1,4 +1,4 @@
#!/bin/bash /usr/scripts/python.sh #! /usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
@ -12,8 +12,9 @@ Licence : GPL v2
import os, sys, time import os, sys, time
import subprocess import subprocess
from lc_ldap import shortcuts sys.path.append('/usr/scripts/gestion')
from gestion.config import upload from ldap_crans import crans_ldap
from config import upload
# logging tools # logging tools
import syslog import syslog
def log(x): def log(x):
@ -29,8 +30,6 @@ import utils.exceptions
import locale import locale
locale.setlocale(locale.LC_TIME, 'fr_FR.UTF-8') locale.setlocale(locale.LC_TIME, 'fr_FR.UTF-8')
# On blackliste 14 jours après que le script ait été éxécuté
DELAY = 14
help = """Script de déconnexion pour mail invalide. help = """Script de déconnexion pour mail invalide.
Une fiche sera générée pour chaque adhérent. Une fiche sera générée pour chaque adhérent.
@ -50,23 +49,22 @@ l'adhérent ayant l'aid 42."""
def generate_ps(proprio, mail): def generate_ps(proprio, mail):
"""On génère la feuille d'avertissement et on retourne son emplacement.""" """On génère la feuille d'avertissement et on retourne son emplacement."""
barcode = "/usr/scripts/admin/mail_invalide/barcode.eps" barcode = "/usr/scripts/admin/mail_invalide/barcode.eps"
name = unicode(proprio['prenom'][0]) + u" " + unicode(proprio['nom'][0])
try: try:
log(u'Generate invalid mail notice for %s' % name) log('Generate invalid mail notice for %s' % proprio.Nom())
# Dossier de génération du ps # Dossier de génération du ps
dossier = '/usr/scripts/var/mails_invalides' dossier = '/usr/scripts/var/mails_invalides'
# Base pour le nom du fichier # Base pour le nom du fichier
fichier = time.strftime('%Y-%m-%d-%H-%M') + '-mail-%s' % (name. fichier = time.strftime('%Y-%m-%d-%H-%M') + '-mail-%s' % (proprio.Nom().
lower().replace(' ', '-')) lower().replace(' ', '-'))
# Création du fichier tex # Création du fichier tex
format_date = '%A %d %B %Y' format_date = '%A %d %B %Y'
with open('%s/mail_invalide.tex' % os.path.dirname(__file__), 'r') as tempfile: with open('%s/mail_invalide.tex' % os.path.dirname(__file__), 'r') as tempfile:
template = tempfile.read() template = tempfile.read()
template = template.replace('~prenom~', proprio['prenom'][0].encode('utf-8')) template = template.replace('~prenom~', proprio.prenom().encode('utf-8'))
template = template.replace('~nom~', proprio['nom'][0].encode('utf-8')) template = template.replace('~nom~', proprio.nom().encode('utf-8'))
template = template.replace('~chambre~', proprio['chbre'][0].encode('utf-8')) template = template.replace('~chambre~', proprio.chbre().encode('utf-8'))
template = template.replace('~mail~', mail.encode('utf-8').replace('_', '\\_')) template = template.replace('~mail~', mail.encode('utf-8').replace('_', '\\_'))
template = template.replace('~fin~', template = template.replace('~fin~',
time.strftime(format_date, time.localtime(time.time()+14*86400))) time.strftime(format_date, time.localtime(time.time()+14*86400)))
@ -85,37 +83,35 @@ def generate_ps(proprio, mail):
except Exception, e: except Exception, e:
log('Erreur lors de la génération du ps : ') log('Erreur lors de la génération du ps : ')
log(str(e)) log(str(e))
log("Values : adherent:%s" % name) log("Values : adherent:%s" % proprio.Nom())
log(utils.exceptions.formatExc()) log(utils.exceptions.formatExc())
raise raise
def set_mail_invalide(adherent, mail, a_verifier, a_imprimer): def set_mail_invalide(adherent, mail, a_verifier, a_imprimer):
name = unicode(adherent['prenom'][0]) + u" " + unicode(adherent['nom'][0]) if adherent.chbre() in ['????', 'EXT']:
if adherent['chbre'][0] in ['????', 'EXT']: print u"Chambre de %s : %s, générer la fiche ? [Yn]" % (adherent.Nom().encode('utf-8'), adherent.chbre())
print u"Chambre de %s : %s, générer la fiche ? [Yn]" % (name, adherent['chbre'][0])
read = '' read = ''
while read not in ['y', 'n']: while read not in ['y', 'n']:
read = raw_input().lower() read = raw_input().lower()
if read == 'n': if read == 'n':
print u"Chambre de %s : %s, impossible de générer la fiche." % (name, adherent['chbre'][0]) print u"Chambre de %s : %s, impossible de générer la fiche." % (adherent.Nom().encode('utf-8'), adherent.chbre())
a_verifier.append(mail) a_verifier.append(mail)
return return
print u"Génération de la fiche pour %s :" % name print "Génération de la fiche pour %s :" % adherent.Nom().encode('utf-8')
fiche = generate_ps(adherent, mail) fiche = generate_ps(adherent, mail)
print fiche print fiche
a_imprimer.append(fiche) a_imprimer.append(fiche)
with adherent as adh: adherent.blacklist([time.time() + 14 * 24 * 3600,
adh.blacklist('mail_invalide','Mail Invalide - Script',debut=int(time.time()) + DELAY * 24 * 3600) '-', 'mail_invalide', "Mail invalide"])
adh.history_gen() adherent.save()
adh.save()
if __name__ == "__main__": if __name__ == "__main__":
if '--help' in sys.argv or '-h' in sys.argv or len(sys.argv) < 2: if '--help' in sys.argv or '-h' in sys.argv or len(sys.argv) < 2:
print help print help
sys.exit(0) sys.exit(0)
ldap = shortcuts.lc_ldap_admin() db = crans_ldap()
# On fait la liste des .forwards dans les homes # On fait la liste des .forwards dans les homes
print " * Lecture des .forward ..." print " * Lecture des .forward ..."
@ -145,24 +141,24 @@ if __name__ == "__main__":
# Est-ce un aid ? # Est-ce un aid ?
if adresse[0] == '-': if adresse[0] == '-':
print " * Recherche de aid=%s ..." % adresse[1:] print " * Recherche de aid=%s ..." % adresse[1:]
res = ldap.search(u"aid=%s" % adresse[1:], mode='rw') res = db.search("aid=%s" % adresse[1:], 'w')['adherent']
if len(res) == 0: if len(res) == 0:
print "*** Erreur : aucun résultat pour aid=%s" % adresse[1:] print "*** Erreur : aucun résultat pour aid=%s" % adresse[1:]
a_verifier.append(adresse) a_verifier.append(adresse)
elif len(res) > 1: elif len(res) > 1:
print "*** Erreur : plusieurs résultats pour aid=%s :" % adresse[1:] print "*** Erreur : plusieurs résultats pour aid=%s :" % adresse[1:]
for adh in res: for adh in res:
print unicode(adh['prenom'][0]) + u" " + unicode(adh['nom'][0]) print adh.Nom()
a_verifier.append(adresse) a_verifier.append(adresse)
else: else:
adherent = res[0] adherent = res[0]
set_mail_invalide(adherent, adherent['mail'][0], a_verifier, a_imprimer) set_mail_invalide(adherent, adherent.email(), a_verifier, a_imprimer)
continue continue
print " * Recherche de %s ..." % adresse print " * Recherche de %s ..." % adresse
# Est-ce un .forward ? # Est-ce un .forward ?
if forwards.has_key(adresse): if forwards.has_key(adresse):
res = ldap.search(u"uid=%s" % forwards[adresse], mode='rw') res = db.search("uid=%s" % forwards[adresse], 'w')['adherent']
if len(res) == 0: if len(res) == 0:
print "*** Erreur : aucun résultat pour uid=%s" % forwards[adresse] print "*** Erreur : aucun résultat pour uid=%s" % forwards[adresse]
a_verifier.append(adresse) a_verifier.append(adresse)
@ -172,18 +168,18 @@ if __name__ == "__main__":
continue continue
# Est-ce une adresse mail sans compte Cr@ns ? # Est-ce une adresse mail sans compte Cr@ns ?
res = ldap.search(u"(|(mail=%s)(mailExt=%s))" % (adresse,adresse), mode='rw') res = db.search("mail=%s" % adresse, 'w')['adherent']
if len(res) == 0: if len(res) == 0:
print "*** Erreur : aucun résultat pour %s" % adresse print "*** Erreur : aucun résultat pour %s" % adresse
a_verifier.append(adresse) a_verifier.append(adresse)
elif len(res) > 1: elif len(res) > 1:
print "*** Erreur : plusieurs résultats pour %s :" % adresse print "*** Erreur : plusieurs résultats pour %s :" % adresse
for adh in res: for adh in res:
print unicode(adh['prenom'][0]) + u" " + unicode(adh['nom'][0]) print adh.Nom()
a_verifier.append(adresse) a_verifier.append(adresse)
else: else:
adherent = res[0] adherent = res[0]
set_mail_invalide(adherent, adresse, a_verifier, a_imprimer) set_mail_invalide(adherent, adherent.email(), a_verifier, a_imprimer)
if len(a_verifier) + len(a_imprimer) > 0: if len(a_verifier) + len(a_imprimer) > 0:
print '' print ''

View file

@ -1,8 +1,9 @@
#!/bin/bash /usr/scripts/python.sh #! /usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys
# Copyright (C) Stéphane Glondu, Alexandre Bos, et autres # Copyright (C) Stéphane Glondu, Alexandre Bos
# Licence : GPLv2 # Licence : GPLv2
__doc__ = u"""Ce script permet de faire le menages parmis les câbleurs qui ne __doc__ = u"""Ce script permet de faire le menages parmis les câbleurs qui ne
@ -20,14 +21,16 @@ Les commandes sont :
import sys, os, re import sys, os, re
import gestion.config sys.path.append('/usr/scripts/gestion')
import config
from email_tools import send_email, parse_mail_template
# Fonctions d'affichage # Fonctions d'affichage
from gestion.affich_tools import coul, tableau, prompt, cprint from affich_tools import coul, tableau, prompt, cprint
# Importation de la base de données # Importation de la base de données
from lc_ldap import shortcuts from ldap_crans import crans_ldap, ann_scol
ldap = shortcuts.lc_ldap_admin() db = crans_ldap()
def _controle_interactif_adherents(liste): def _controle_interactif_adherents(liste):
""" """
@ -47,28 +50,26 @@ def _controle_interactif_adherents(liste):
nb = 0 nb = 0
for a in liste: for a in liste:
ok = prompt(u'[%3d] %s, %s (%s) ?' ok = prompt(u'[%3d] %s, %s (%s) ?'
% (restant, a['nom'][0], a['prenom'][0], a['aid'][0]), 'n', '').lower() % (restant, a.nom(), a.prenom(), a.id()), 'n', '').lower()
restant -= 1 restant -= 1
if ok == 'o': if ok == 'o':
modifiable = ldap.search(u'aid=%s' % a['aid'][0], mode='rw')[0] modifiable = db.search('aid=%s' % a.id(), 'w')['adherent'][0]
try: if modifiable._modifiable:
with modifiable as adh: modifiable.droits([])
adh['droits'].remove(u'Cableur') cprint(modifiable.save())
adh.history_gen() else:
adh.save() cprint(u'Adhérent %s locké, réessayer plus tard' % modifiable.Nom(), 'rouge')
cprint(u'Droits cableurs retirés', 'rouge')
except:
cprint(u'Adhérent %s locké, réessayer plus tard' % modifiable['nom'][0], 'rouge')
elif ok != 'n': elif ok != 'n':
cprint(u'Arrêt du contrôle %s des membres actifs' % explicite, 'rouge') cprint(u'Arrêt du contrôle %s des membres actifs' % explicite, 'rouge')
break break
def candidats(): def candidats():
todo_list1 = ldap.search(u'droits=cableur') todo_list1 = db.search('droits=*')['adherent']
todo_list = [] todo_list = []
for adh in todo_list1: for adh in todo_list1:
if not adh.paiement_ok(): if adh.droitsGeles():
todo_list.append(adh) todo_list.append(adh)
todo_list.sort(lambda x, y: cmp((x.nom(), x.prenom()), (y.nom(), y.prenom())))
return todo_list return todo_list
def lister(): def lister():
@ -79,7 +80,7 @@ def lister():
print "Liste des câbleur dont la cotisation n'est pas à jour." print "Liste des câbleur dont la cotisation n'est pas à jour."
print print
for adh in todo_list: for adh in todo_list:
print unicode(adh['prenom'][0]) + u" " + unicode(adh['nom'][0]) print adh.prenom() + " " + adh.nom()
print print
print "total : " + str(len(todo_list)) print "total : " + str(len(todo_list))

View file

@ -1,4 +1,4 @@
#!/bin/bash /usr/scripts/python.sh #!/usr/bin/python
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# #
# total_impression.py # total_impression.py
@ -6,7 +6,6 @@
# #
# Copyright (C) 2007 Michel Blockelet <blockelet@crans.org> # Copyright (C) 2007 Michel Blockelet <blockelet@crans.org>
# #
# Revu et corrigé en 2015 par Gabriel Détraz
# This file is free software; you can redistribute it and/or modify # This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or # the Free Software Foundation; either version 2 of the License, or
@ -33,11 +32,14 @@ Options :
Les dates doivent etre de la forme jj/mm/aaaa.""" Les dates doivent etre de la forme jj/mm/aaaa."""
import sys import sys
from lc_ldap import shortcuts sys.path.append("/usr/scripts/gestion/")
from gestion.affich_tools import cprint from ldap_crans import crans_ldap
from config import ann_scol
from affich_tools import cprint
import time import time
ldap = shortcuts.lc_ldap_admin() db = crans_ldap()
date_debut_ann_scol = time.mktime((ann_scol, 8, 1, 0, 0, 0, 0, 0, 0))
def datestrtoint(strdate): def datestrtoint(strdate):
u""" Convertit une date en entier. """ u""" Convertit une date en entier. """
@ -50,7 +52,7 @@ def soldes_adherent(dlinf, dlsup, adherent, verbose):
totaldebit = 0 totaldebit = 0
totalcredit = 0 totalcredit = 0
for hist in adherent['historique']: for hist in adherent.historique():
sep = ' ' sep = ' '
champ = hist.replace(',', '').replace(': ', '').split(sep) champ = hist.replace(',', '').replace(': ', '').split(sep)
if datestrtoint(champ[0]) >= dlinf and (dlsup == 0 or datestrtoint(champ[0]) <= dlsup): if datestrtoint(champ[0]) >= dlinf and (dlsup == 0 or datestrtoint(champ[0]) <= dlsup):
@ -110,23 +112,19 @@ def calcul_soldes():
totaldebit = 0 totaldebit = 0
totalcredit = 0 totalcredit = 0
liste = ldap.search(u"uid=*",sizelimit=10000) liste = db.search("login=*")['adherent']
for adherent in liste: for adherent in liste:
try: adhdebit, adhcredit = soldes_adherent(dlinf, dlsup, adherent, verbose)
adhdebit, adhcredit = soldes_adherent(dlinf, dlsup, adherent, verbose) if adhdebit + adhcredit > 0 and adhdebit + adhcredit < 1000000: # On evite Toto Passoir
if adhdebit + adhcredit > 0 and adhdebit + adhcredit < 1000000: # On evite Toto Passoir if verbose >= 2:
if verbose >= 2: cprint('-' * 40, 'cyan')
cprint('-' * 40, 'cyan') if verbose >= 1:
if verbose >= 1: cprint('Debit total pour ' + adherent.Nom() + ' : ' + str(adhdebit) + ' euros', 'rouge')
name = unicode(adherent['prenom'][0]) + u" " + unicode(adherent['nom'][0]) cprint('Credit total pour ' + adherent.Nom() + ' : ' + str(adhcredit) + ' euros', 'vert')
cprint(u'Debit total pour ' + name + u' : ' + unicode(adhdebit) + u' euros', 'rouge') cprint('=' * 40, 'bleu')
cprint(u'Credit total pour ' + name + u' : ' + unicode(adhcredit) + u' euros', 'vert') totaldebit += adhdebit
cprint('=' * 40, 'bleu') totalcredit += adhcredit
totaldebit += adhdebit
totalcredit += adhcredit
except KeyError:
pass
if verbose >= 1: if verbose >= 1:
cprint('=' * 80, 'bleu') cprint('=' * 80, 'bleu')
if dlinf == 0: if dlinf == 0:

View file

@ -1,4 +1,4 @@
#!/bin/bash /usr/scripts/python.sh #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" Envoie un mail avec la liste des serveurs qui ne sont pas synchro avec bcfg2. """ Envoie un mail avec la liste des serveurs qui ne sont pas synchro avec bcfg2.
@ -43,6 +43,7 @@ if __name__ == "__main__":
debug = "--debug" in sys.argv debug = "--debug" in sys.argv
if "--mail" in sys.argv: if "--mail" in sys.argv:
if hosts != "": if hosts != "":
sys.path.append("/usr/scripts/")
import utils.sendmail import utils.sendmail
utils.sendmail.sendmail("root@crans.org", "roots@crans.org", u"Serveurs non synchronisés avec bcfg2", hosts, more_headers={"X-Mailer" : "bcfg2-reports"}, debug=debug) utils.sendmail.sendmail("root@crans.org", "roots@crans.org", u"Serveurs non synchronisés avec bcfg2", hosts, more_headers={"X-Mailer" : "bcfg2-reports"}, debug=debug)
elif debug: elif debug:

View file

@ -4,13 +4,19 @@ if [[ $1 = "" ]] || [[ $1 = $USER ]] ; then
/usr/bin/quota /usr/bin/quota
else else
/usr/bin/quota $* /usr/bin/quota $*
fi | sed 's/home-adh/home/' | awk -F'(:| *)' ' fi | awk -F'(:| *)' '
BEGIN { fs = "" } BEGIN { fs = "" }
/Disk/ { print; print "utilisé\tquota\tlimite\t%\t(en Mo)" } /Disk/ { print; print "utilisé\tquota\tlimite\t%\t(en Mo)" }
{ {
if (NF == 2) { fs = $2 } if (NF == 2) { fs = $2 }
else if (fs != "") { else if (fs != "") {
#unit = 512/1024
#system(/usr/bin/stat -c %B " fs) | getline unit
#if (fs == "/home") { total = 400 }
#else if (fs == "/var/mail") { total = 75 }
#else { total = 100 }
printf "%3.2f\t%3.2f\t%3.2f\t%3.1f\t%s\n", $2/1024, $3/1024, $4/1024, $2*100/$3, fs printf "%3.2f\t%3.2f\t%3.2f\t%3.1f\t%s\n", $2/1024, $3/1024, $4/1024, $2*100/$3, fs
#printf "%3.2f\t%3.2f\t%3.2f\t%3.1f\t%s\n", $2/1024*unit, $3/1024*unit, $4/1024*unit, $2*100/$3, fs
fs = "" fs = ""
} }
}' }'

View file

@ -70,13 +70,10 @@ BL_REJECT = [u'bloq']
BL_ISOLEMENT = [u'virus', u'autodisc_virus', u'autodisc_p2p', u'ipv6_ra'] BL_ISOLEMENT = [u'virus', u'autodisc_virus', u'autodisc_p2p', u'ipv6_ra']
#: place sur accueil #: place sur accueil
BL_ACCUEIL = [u'paiement'] BL_ACCUEIL = []
# À classer: # Ces blacklists ont des effets soft (portail captif port 80)
# [u'carte_etudiant', u'chambre_invalide', ] #BL_ACCUEIL = [u'carte_etudiant', u'chambre_invalide', u'paiement']
# TODO: mettre ça dans config.py en explicitant un peu comment ça marche
# et en trouvant moyen de refresh en fonction de la période de l'année
# (bl soft/hard parefeu ou pas)
#: chambre qui n'en sont pas vraiment. Il s'agit de prises en libre accès, #: chambre qui n'en sont pas vraiment. Il s'agit de prises en libre accès,
# pour lequelles il est donc idiot d'activer la protection antisquattage: # pour lequelles il est donc idiot d'activer la protection antisquattage:
@ -104,14 +101,11 @@ def radius_event(fun):
tuples en entrée en un dictionnaire.""" tuples en entrée en un dictionnaire."""
def new_f(auth_data): def new_f(auth_data):
if type(auth_data) == dict: data = dict()
data = auth_data for (key, value) in auth_data or []:
else: # Beware: les valeurs scalaires sont entre guillemets
data = dict() # Ex: Calling-Station-Id: "une_adresse_mac"
for (key, value) in auth_data or []: data[key] = value.replace('"', '')
# Beware: les valeurs scalaires sont entre guillemets
# Ex: Calling-Station-Id: "une_adresse_mac"
data[key] = value.replace('"', '')
try: try:
return fun(data) return fun(data)
except Exception as err: except Exception as err:
@ -219,7 +213,7 @@ def get_prise_chbre(data):
def realm_of_machine(machine): def realm_of_machine(machine):
"""Renvoie le `realm` d'une machine. Don't ask""" """Renvoie le `realm` d'une machine. Don't ask"""
if isinstance(machine, lc_ldap.objets.machineFixe): if isinstance(machine, lc_ldap.objets.machineFixe):
return 'adherents' return 'fil'
elif isinstance(machine, lc_ldap.objets.machineWifi): elif isinstance(machine, lc_ldap.objets.machineWifi):
return 'wifi-adh' return 'wifi-adh'
else: else:
@ -277,18 +271,6 @@ def instantiate(*_):
if TEST_SERVER: if TEST_SERVER:
logger.info('DBG_FREERADIUS is enabled') logger.info('DBG_FREERADIUS is enabled')
@radius_event
def authorize(data):
"""Fonction qui aiguille entre nas, wifi et filaire pour authorize
On se contecte de faire une verification basique de ce que contien la requète
pour déterminer la fonction à utiliser"""
if data.get('NAS-Port-Type', '')==u'Ethernet':
return authorize_fil(data)
elif u"Wireless" in data.get('NAS-Port-Type', ''):
return authorize_wifi(data)
else:
return authorize_nas(data)
@radius_event @radius_event
def authorize_wifi(data): def authorize_wifi(data):
"""Section authorize pour le wifi """Section authorize pour le wifi
@ -373,7 +355,7 @@ def authorize_fil(data):
return (radiusd.RLM_MODULE_UPDATED, return (radiusd.RLM_MODULE_UPDATED,
(), (),
( (
("Auth-Type", "Accept"), ("Auth-Type", "crans_fil"),
), ),
) )
@ -446,15 +428,6 @@ def authorize_nas(data, ldap):
("FreeRADIUS-Client-Virtual-Server", vserver), ("FreeRADIUS-Client-Virtual-Server", vserver),
), ),
) )
@radius_event
def post_auth(data):
# On cherche quel est le type de machine, et quel sites lui appliquer
if data.get('NAS-Port-Type', '')==u'Ethernet':
return post_auth_fil(data)
elif u"Wireless" in data.get('NAS-Port-Type', ''):
return post_auth_wifi(data)
@radius_event @radius_event
def post_auth_wifi(data): def post_auth_wifi(data):
"""Appelé une fois que l'authentification est ok. """Appelé une fois que l'authentification est ok.

View file

@ -0,0 +1 @@
../rlm_python_fil.conf

View file

@ -0,0 +1 @@
../rlm_python_nas.conf

View file

@ -1 +0,0 @@
../rlm_python_unifie.conf

View file

@ -0,0 +1 @@
../rlm_python_wifi.conf

View file

@ -0,0 +1,37 @@
# Configuration for the Python module.
#
#
python crans_fil {
mod_instantiate = freeradius.auth
func_instantiate = instantiate
# Spécifique au filaire: accepte direct
mod_authorize = freeradius.auth
func_authorize = authorize_fil
# Renseigne le vlan
# remplacer par dummy_fun pour ignorer le tagging de vlan
mod_post_auth = freeradius.auth
func_post_auth = post_auth_fil
# Que faire avant de quitter
mod_detach = freeradius.auth
func_detach = detach
# Le reste est dumb et inutile
mod_accounting = freeradius.auth
func_accounting = dummy_fun
mod_pre_proxy = freeradius.auth
func_pre_proxy = dummy_fun
mod_post_proxy = freeradius.auth
func_post_proxy = dummy_fun
mod_recv_coa = freeradius.auth
func_recv_coa = dummy_fun
mod_send_coa = freeradius.auth
func_send_coa = dummy_fun
}

View file

@ -0,0 +1,35 @@
# Configuration for the Python module.
#
#
python crans_nas {
mod_instantiate = freeradius.auth
func_instantiate = instantiate
# Spécifique NAS : rempli le mdp
mod_authorize = freeradius.auth
func_authorize = authorize_nas
# Que faire avant de quitter
mod_detach = freeradius.auth
func_detach = detach
# Le reste est dumb et inutile
mod_post_auth = freeradius.auth
func_post_auth = dummy_fun
mod_accounting = freeradius.auth
func_accounting = dummy_fun
mod_pre_proxy = freeradius.auth
func_pre_proxy = dummy_fun
mod_post_proxy = freeradius.auth
func_post_proxy = dummy_fun
mod_recv_coa = freeradius.auth
func_recv_coa = dummy_fun
mod_send_coa = freeradius.auth
func_send_coa = dummy_fun
}

View file

@ -1,38 +0,0 @@
# Configuration for the Python module.
#
#
python crans_unifie {
mod_instantiate = freeradius.auth
func_instantiate = instantiate
# Pour le authorize, c'est auth.py qui fait le tri maintenant
mod_authorize = freeradius.auth
func_authorize = authorize
# Renseigne le vlan si necessaire
# remplacer par dummy_fun pour ignorer le tagging de vlan
mod_post_auth = freeradius.auth
func_post_auth = post_auth
# Que faire avant de quitter
mod_detach = freeradius.auth
func_detach = detach
# Le reste sert à rien
mod_accounting = freeradius.auth
func_accounting = dummy_fun
mod_pre_proxy = freeradius.auth
func_pre_proxy = dummy_fun
mod_post_proxy = freeradius.auth
func_post_proxy = dummy_fun
mod_recv_coa = freeradius.auth
func_recv_coa = dummy_fun
mod_send_coa = freeradius.auth
func_send_coa = dummy_fun
}

View file

@ -0,0 +1,37 @@
# Configuration for the Python module.
#
#
python crans_wifi {
mod_instantiate = freeradius.auth
func_instantiate = instantiate
# Spécifique au WiFi : rempli le mdp
mod_authorize = freeradius.auth
func_authorize = authorize_wifi
# Renseigne le vlan
# remplacer par dummy_fun pour ignorer le tagging de vlan
mod_post_auth = freeradius.auth
func_post_auth = post_auth_wifi
# Que faire avant de quitter
mod_detach = freeradius.auth
func_detach = detach
# Le reste est dumb et inutile
mod_accounting = freeradius.auth
func_accounting = dummy_fun
mod_pre_proxy = freeradius.auth
func_pre_proxy = dummy_fun
mod_post_proxy = freeradius.auth
func_post_proxy = dummy_fun
mod_recv_coa = freeradius.auth
func_recv_coa = dummy_fun
mod_send_coa = freeradius.auth
func_send_coa = dummy_fun
}

View file

@ -11,7 +11,7 @@ server dynamic_clients {
update request { update request {
NAS-Identifier = "%{Packet-Src-IP-Address:-%{Packet-Src-IPv6-Address}}" NAS-Identifier = "%{Packet-Src-IP-Address:-%{Packet-Src-IPv6-Address}}"
} }
crans_unifie crans_nas
} }
} }

View file

@ -7,14 +7,14 @@
server filaire { server filaire {
authorize{ authorize{
preprocess preprocess
crans_unifie crans_fil
} }
authenticate{ authenticate{
crans_unifie crans_fil
} }
post-auth{ post-auth{
crans_unifie crans_fil
} }
} }

View file

@ -21,7 +21,7 @@ server inner-tunnel {
authorize { authorize {
#preprocess #preprocess
crans_unifie crans_wifi
# #
# The chap module will set 'Auth-Type := CHAP' if we are # The chap module will set 'Auth-Type := CHAP' if we are
# handling a CHAP request and Auth-Type has not already been set # handling a CHAP request and Auth-Type has not already been set
@ -237,7 +237,7 @@ session {
# Once we KNOW that the user has been authenticated, there are # Once we KNOW that the user has been authenticated, there are
# additional steps we can take. # additional steps we can take.
post-auth { post-auth {
crans_unifie crans_wifi
# Note that we do NOT assign IP addresses here. # Note that we do NOT assign IP addresses here.
# If you try to assign IP addresses for EAP authentication types, # If you try to assign IP addresses for EAP authentication types,

View file

@ -558,26 +558,7 @@ if __name__ == "__main__":
time.sleep(1) time.sleep(1)
prettyDoin("Les carottes sont cuites." , "Ok") prettyDoin("Les carottes sont cuites." , "Ok")
data = [ data = [[style("Durand", "rouge"), "Toto", "40", "50 rue Döp"], ["Dupont", "Robert", "50", "42" + style(" avenue ", "vert") + style("dumotel", 'rouge')], [style("znvuzbvzruobouzb", ["gras", "vert"]), "pppoe", "1", "poiodur 50 pepe"]]
[
style("Durand", "rouge"),
"Toto",
"40",
"50 rue Döp"
],
[
"Dupont",
"Robert",
"50",
"42" + style(" avenue ", "vert") + style("dumotel", 'rouge')
],
[
style("znvuzbvzruobouzb", ["gras", "vert"]),
"pppoe",
"1",
"poiodur 50 pepe"
]
]
titres = ("Nom", "Prénom", "Âge", "Adresse") titres = ("Nom", "Prénom", "Âge", "Adresse")
longueurs = [25, 25, '*', '*'] longueurs = [25, 25, '*', '*']
print tableau(data, titres, longueurs).encode(guess_preferred_encoding()) print tableau(data, titres, longueurs).encode(guess_preferred_encoding())

View file

@ -2,11 +2,8 @@
import os import os
import psycopg2 import psycopg2
from functools import wraps from functools import wraps
import time import time
import socket
conn = None conn = None
# : échec définitif, on raise une exception direct # : échec définitif, on raise une exception direct
@ -137,8 +134,7 @@ uplink_prises={ 'a' :
349 : 'uplink->batb-4', 350 : 'libre-service', 349 : 'uplink->batb-4', 350 : 'libre-service',
401 : 'uplink->batb-0', 402 : 'uplink->batb-1', 401 : 'uplink->batb-0', 402 : 'uplink->batb-1',
403 : 'uplink->batb-2', 404 : 'uplink->batb-3', 403 : 'uplink->batb-2', 404 : 'uplink->batb-3',
405 : 'uplink->backbone', 523 : 'uplink->batb-4', 405 : 'uplink->backbone' },
},
'c' : 'c' :
{ 49 : 'uplink->batc-3', 50 : 'libre-service', { 49 : 'uplink->batc-3', 50 : 'libre-service',
149 : 'uplink->batc-3', 150 : 'libre-service', 149 : 'uplink->batc-3', 150 : 'libre-service',
@ -282,26 +278,6 @@ _HIDDEN_SWITCHES = [
'batv-0.adm.crans.org', 'batv-0.adm.crans.org',
] ]
def guess_switch_fqdn(switch_name):
"""Retourne le FQDN d'un switch à partir de son nom"""
try:
return socket.gethostbyname_ex(switch_name)[0]
except socket.gaierror:
pass
try:
return socket.gethostbyname_ex(switch_name + ".adm.crans.org")[0]
except socket.gaierror:
pass
try:
return socket.gethostbyname_ex(switch_name + ".crans.org")[0]
except socket.gaierror:
pass
raise socket.gaierror
def all_switchs(bat=None, hide=_SPECIAL_SWITCHES + _HIDDEN_SWITCHES): def all_switchs(bat=None, hide=_SPECIAL_SWITCHES + _HIDDEN_SWITCHES):
"""Retourne la liste des switchs pour un batiment. """Retourne la liste des switchs pour un batiment.
@ -318,12 +294,7 @@ def all_switchs(bat=None, hide=_SPECIAL_SWITCHES + _HIDDEN_SWITCHES):
for b in bat: for b in bat:
indexes = set(n/100 for n in uplink_prises[b]) indexes = set(n/100 for n in uplink_prises[b])
for i in indexes: for i in indexes:
switch_name = "bat%s-%s" % (b, i) hostname = "bat%s-%s.adm.crans.org" % (b, i)
try:
hostname = guess_switch_fqdn(switch_name)
except socket.gaierror:
print "Le switch %s ne semble pas exister." % (switch_name,)
continue
if hostname not in hide: if hostname not in hide:
switchs.append(hostname) switchs.append(hostname)
# on ajoute quand-même le backbone et/ou multiprise-v6 si demandé # on ajoute quand-même le backbone et/ou multiprise-v6 si demandé

View file

@ -18,10 +18,11 @@ import re
import affichage import affichage
import lc_ldap.shortcuts import lc_ldap.shortcuts
from lc_ldap.crans_utils import to_generalized_time_format as to_gtf
import mail as mail_module import mail as mail_module
from config import demenagement_delai as delai, \ from config import demenagement_delai as delai, \
gtf_debut_periode_transitoire, periode_transitoire debut_periode_transitoire, periode_transitoire
ERASE_DAY = { 'second': 0, 'minute': 0, 'microsecond': 0, 'hour': 0, } ERASE_DAY = { 'second': 0, 'minute': 0, 'microsecond': 0, 'hour': 0, }
DAY = datetime.timedelta(days=1) DAY = datetime.timedelta(days=1)
@ -71,28 +72,16 @@ def warn_or_delete(smtp, clandestin, fail, done):
mail_addr = clandestin.get_mail() mail_addr = clandestin.get_mail()
if not clandestin.machines() or not mail_addr: if not clandestin.machines() or not mail_addr:
return # Si pas de machine, on s'en fout. Si pas de mail, inutile return # Si pas de machine, on s'en fout. Si pas de mail, inutile
try:
data = {
'dn': clandestin.dn.split(',')[0],
'when': now.strftime('%Y/%M/%D %H:%m:%S:%s'),
'chbre' : exchambre,
}
chbre_url = mail_module.validation_url('demenagement', data, True)
chbre_url_error = u""
except Exception as error:
chbre_url_error = u"[[erreur de génération: %r]]" % error
chbre_url = u""
data = { data = {
"from" : RESP, "from" : RESP,
"chambre" : exchambre, "chambre" : exchambre,
"jours" : (date_suppr - now).days+1, "jours" : (date_suppr - now).days+1,
"to" : mail_addr, "to" : mail_addr,
"adh": clandestin, "adh": clandestin,
"chbre_url" : chbre_url,
"chbre_url_error" : chbre_url_error,
"lang_info": "English version below", "lang_info": "English version below",
} }
smtp.send_template('demenagement', data) mail = mail_module.generate('demenagement', data)
smtp.sendmail(RESP, [mail_addr], mail.as_string())
def format_entry(m): def format_entry(m):
"""Renvoie une ligne de tableau, pour une machine""" """Renvoie une ligne de tableau, pour une machine"""
@ -112,7 +101,7 @@ if __name__ == '__main__':
conn = lc_ldap.shortcuts.lc_ldap_admin() conn = lc_ldap.shortcuts.lc_ldap_admin()
if periode_transitoire: if periode_transitoire:
date = gtf_debut_periode_transitoire date = to_gtf(debut_periode_transitoire)
else: else:
date = now.strftime(FORMAT_LDAP) + 'Z' date = now.strftime(FORMAT_LDAP) + 'Z'

View file

@ -25,6 +25,7 @@ import lc_ldap.attributs
import lc_ldap.objets import lc_ldap.objets
import gestion.mail as mail_module import gestion.mail as mail_module
encoding = getattr(sys.stdout, 'encoding', "UTF-8")
current_user = os.getenv("SUDO_USER") or os.getenv("USER") or os.getenv("LOGNAME") or getpass.getuser() current_user = os.getenv("SUDO_USER") or os.getenv("USER") or os.getenv("LOGNAME") or getpass.getuser()
def check_password(password, no_cracklib=False, dialog=False): def check_password(password, no_cracklib=False, dialog=False):
@ -40,14 +41,10 @@ def check_password(password, no_cracklib=False, dialog=False):
problem = True problem = True
msg += u"Le mot de passe ne doit contenir que des caractères ascii.\n" msg += u"Le mot de passe ne doit contenir que des caractères ascii.\n"
if len(password) >= 64:
problem = True
msg += u"Le mot de passe doit faire strictement moins de 64 caractères\n"
# Nounou mode # Nounou mode
if no_cracklib: if no_cracklib:
if len(password) >= config.password.root_min_len: if len(password) >= config.password.root_min_len:
return True, msg return True
else: else:
upp = 0 upp = 0
low = 0 low = 0
@ -100,7 +97,7 @@ def check_password(password, no_cracklib=False, dialog=False):
msg = affich_tools.coul(msg, 'rouge', dialog=dialog) msg = affich_tools.coul(msg, 'rouge', dialog=dialog)
return True, msg return True, msg
except ValueError as e: except ValueError as e:
msg += str(e).decode(config.in_encoding) msg += str(e).decode()
if dialog: if dialog:
msg = affich_tools.coul(msg, 'rouge', dialog=dialog) msg = affich_tools.coul(msg, 'rouge', dialog=dialog)
@ -119,23 +116,16 @@ def check_password(password, no_cracklib=False, dialog=False):
return False, msg return False, msg
@lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5, constructor=lc_ldap.shortcuts.lc_ldap_admin) @lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5, constructor=lc_ldap.shortcuts.lc_ldap_admin)
def change_password(ldap, login=None, verbose=False, no_cracklib=False, **kwargs): def change_password(ldap, login=None, verbose=False, no_cracklib=False, **args):
""" """
Change le mot de passe en fonction des arguments Change le mot de passe en fonction des arguments
""" """
if login is None: if login is None:
login = current_user login = current_user
if type(login) == str: if type(login) == str:
login = login.decode(config.in_encoding) login = login.decode(encoding)
if no_cracklib:
if not lc_ldap.attributs.nounou in ldap.droits:
no_cracklib = False
login = lc_ldap.crans_utils.escape(login) login = lc_ldap.crans_utils.escape(login)
query = ldap.search(u"(uid=%s)" % login, mode="w") query = ldap.search(u"(uid=%s)" % login, mode="w")
if not query: if not query:
affich_tools.cprint('Utilisateur introuvable dans la base de données, modification de l\'utilisateur local.', "rouge") affich_tools.cprint('Utilisateur introuvable dans la base de données, modification de l\'utilisateur local.', "rouge")
sys.exit(2) sys.exit(2)
@ -145,7 +135,7 @@ def change_password(ldap, login=None, verbose=False, no_cracklib=False, **kwargs
user['userPassword'] = [lc_ldap.crans_utils.hash_password("test").decode('ascii')] user['userPassword'] = [lc_ldap.crans_utils.hash_password("test").decode('ascii')]
user.cancel() user.cancel()
except EnvironmentError as e: except EnvironmentError as e:
affich_tools.cprint(str(e).decode(config.in_encoding), "rouge") affich_tools.cprint(str(e).decode(encoding), "rouge")
# Génération d'un mail # Génération d'un mail
From = 'roots@crans.org' From = 'roots@crans.org'
@ -155,11 +145,11 @@ To: %s
Subject: Tentative de changement de mot de passe ! Subject: Tentative de changement de mot de passe !
Tentative de changement du mot de passe de %s par %s. Tentative de changement du mot de passe de %s par %s.
""" % (From, To, login.encode(config.out_encoding), current_user) """ % (From, To , login.encode(encoding), current_user)
# Envoi mail # Envoi mail
with mail_module.ServerConnection() as conn: with mail_module.ServerConnection() as conn:
conn.sendmail(From, To, mail) conn.sendmail(From, To , mail )
sys.exit(1) sys.exit(1)
# On peut modifier le MDP # On peut modifier le MDP
@ -167,50 +157,44 @@ Tentative de changement du mot de passe de %s par %s.
prenom = "Club" prenom = "Club"
else: else:
prenom = user['prenom'][0] prenom = user['prenom'][0]
affich_tools.cprint( affich_tools.cprint("Changement du mot de passe de %s %s." %
"Changement du mot de passe de %s %s." % ( (prenom, user['nom'][0]),
prenom, "vert")
user['nom'][0]
),
"vert",
)
# Règles du jeu # Règles du jeu
# (J'ai perdu) # (J'ai perdu)
if verbose: if verbose:
affich_tools.cprint( affich_tools.cprint(u"""Règles :
u"""Règles :
Longueur standard : %s, root : %s, Longueur standard : %s, root : %s,
Minimums : chiffres : %s, minuscules : %s, majuscules : %s, autres : %s, Minimums : chiffres : %s, minuscules : %s, majuscules : %s, autres : %s,
Scores de longueur : chiffres : %s, minuscules : %s, majuscules : %s, autres : %s, Scores de longueur : chiffres : %s, minuscules : %s, majuscules : %s, autres : %s,
Cracklib : %s.""" % (config.password.min_len, Cracklib : %s.""" % (
config.password.root_min_len, config.password.min_len,
config.password.min_cif, config.password.root_min_len,
config.password.min_low, config.password.min_cif,
config.password.min_upp, config.password.min_low,
config.password.min_oth, config.password.min_upp,
config.password.cif_value, config.password.min_oth,
config.password.low_value, config.password.cif_value,
config.password.upp_value, config.password.low_value,
config.password.oth_value, config.password.upp_value,
"Oui" * (not no_cracklib) + "Non" * (no_cracklib), config.password.oth_value,
), "Oui" * (not no_cracklib) + "Non" * (no_cracklib)
'jaune', ),
) 'jaune')
else: else:
affich_tools.cprint( affich_tools.cprint(u"""Le nouveau mot de passe doit comporter au minimum %s caractères.
u"""Le nouveau mot de passe doit comporter au minimum %s caractères.
Il ne doit pas être basé sur un mot du dictionnaire. Il ne doit pas être basé sur un mot du dictionnaire.
Il doit contenir au moins %s chiffre(s), %s minuscule(s), Il doit contenir au moins %s chiffre(s), %s minuscule(s),
%s majuscule(s) et au moins %s autre(s) caractère(s). %s majuscule(s) et au moins %s autre(s) caractère(s).
CTRL+D ou CTRL+C provoquent un abandon.""" % (config.password.min_len, CTRL+D ou CTRL+C provoquent un abandon.""" %
config.password.min_cif, (
config.password.min_low, config.password.min_len,
config.password.min_upp, config.password.min_cif,
config.password.min_oth config.password.min_low,
), config.password.min_upp,
'jaune', config.password.min_oth
) ), 'jaune')
try: try:
while True: while True:
@ -240,35 +224,29 @@ CTRL+D ou CTRL+C provoquent un abandon.""" % (config.password.min_len,
affich_tools.cprint(u"Mot de passe de %s changé." % (user['uid'][0]), "vert") affich_tools.cprint(u"Mot de passe de %s changé." % (user['uid'][0]), "vert")
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Recherche dans la base des adhérents", parser = argparse.ArgumentParser(
add_help=False, description="Recherche dans la base des adhérents",
) add_help=False)
parser.add_argument('-h', parser.add_argument('-h', '--help',
'--help', help="Affiche ce message et quitte.",
help="Affiche ce message et quitte.", action="store_true")
action="store_true", parser.add_argument('-n', '--no-cracklib',
) help="Permet de contourner les règles de choix du mot de passe" +
parser.add_argument('-n', "(réservé aux nounous).",
'--no-cracklib', action="store_true")
help="Permet de contourner les règles de choix du mot de passe" + parser.add_argument('-v', '--verbose',
"(réservé aux nounous).", help="Permet de contourner les règles de choix du mot de passe" +
action="store_true", "(réservé aux nounous).",
) action="store_true")
parser.add_argument('-v', parser.add_argument('login', type=str, nargs="?",
'--verbose', help="L'utilisateur dont on veut changer le mot de passe.")
help="Permet de contourner les règles de choix du mot de passe" +
"(réservé aux nounous).",
action="store_true",
)
parser.add_argument('login',
type=str,
nargs="?",
help="L'utilisateur dont on veut changer le mot de passe.",
)
args = parser.parse_args() args = parser.parse_args()
if args.help: if args.help:
parser.print_help() parser.print_help()
sys.exit(0) sys.exit(0)
if args.no_cracklib:
if not lc_ldap.attributs.nounou in ldap.droits:
args.no_cracklib = False
change_password(**vars(args)) change_password(**vars(args))

View file

@ -10,28 +10,29 @@
import os, sys import os, sys
from gestion.affich_tools import prompt from gestion.affich_tools import prompt
from gestion.ldap_crans import crans_ldap
from lc_ldap import shortcuts db = crans_ldap()
ldap = shortcuts.lc_ldap_admin()
uid = os.getenv('SUDO_UID') uid = os.getenv('SUDO_UID')
if not uid : if not uid :
print "Impossible de déterminer l'utilisateur" print "Impossible de déterminer l'utilisateur"
sys.exit(1) sys.exit(1)
adh = ldap.search(u'uidNumber=%s' % uid,mode='w') s = db.search('uidNumber=%s' % os.getenv('SUDO_UID'),'w')
try:
adh = adh[0]
except IndexError:
print 'Erreur fatale lors de la consultation de la base LDAP'
sys.exit(3)
# On vérifie que c'est pas un club # On vérifie que c'est pas un club
if unicode(adh.ldap_name)!=u"adherent": club = s['club']
if len(club) == 1 :
print 'Pas de changement de shell pour les clubs' print 'Pas de changement de shell pour les clubs'
sys.exit(2) sys.exit(2)
# On regarde si on a des résultats dans les adhérents
adh = s['adherent']
if len(adh) != 1 :
print 'Erreur fatale lors de la consultation de la base LDAP'
sys.exit(3)
adh = adh[0]
shell = prompt(u'Nouveau shell :') shell = prompt(u'Nouveau shell :')
fd=open('/etc/shells') fd=open('/etc/shells')
lines=fd.readlines() lines=fd.readlines()
@ -44,9 +45,7 @@ if not shell in shells:
print '\n'.join(shells) print '\n'.join(shells)
sys.exit(4) sys.exit(4)
with adh as ad: adh.chsh(shell)
ad['loginShell']=shell adh.save()
ad.save()
# A cause de nscd # A cause de nscd
print "La modification sera prise en compte dans l'heure suivante." print "La modification sera prise en compte dans l'heure suivante."

View file

@ -4,4 +4,3 @@
from config import * from config import *
from encoding import * from encoding import *
import dns

View file

@ -9,35 +9,34 @@ import datetime
# Fichier généré à partir de bcfg2 # Fichier généré à partir de bcfg2
from config_srv import adm_only, role from config_srv import adm_only, role
# Valeur par défaut pour les champs d'études
etudes_defaults = [
u"Établissement inconnu",
u"Année inconnue",
u"Domaine d'études inconnu"
]
gtfepoch = "19700101000000Z" gtfepoch = "19700101000000Z"
##### Gestion des câblages ##### Gestion des câblages
# Selon la date, on met : # Selon la date, on met :
# -ann_scol : Année scolaire en cours
# -periode_transitoire : on accepte ceux qui ont payé l'année dernière # -periode_transitoire : on accepte ceux qui ont payé l'année dernière
# On récupère l'année scolaire à tout besoin # Ne modifier que les dates !
__annee = time.localtime()[0] dat = time.localtime()
if dat[1] < 8 or dat[1] == 8 and dat[2] < 16:
# Si pas encore début août, on est dans l'année précédente
ann_scol = dat[0]-1
periode_transitoire = False
# sinon on change d'année
elif dat[1] < 10:
# Si pas encore octobre, les gens ayant payé l'année précédente sont
# acceptés
ann_scol = dat[0]
periode_transitoire = True
else:
# Seulement ceux qui ont payé cette année sont acceptés
ann_scol = dat[0]
periode_transitoire = False
# Prochaine période transitoire de l'année version generalizedTimeFormat debut_periode_transitoire = time.mktime(time.strptime("%s/08/16 00:00:00" % (ann_scol,), "%Y/%m/%d %H:%M:%S"))
gtf_debut_periode_transitoire = "%s0816000000+0200" % (__annee,) fin_periode_transitoire = time.mktime(time.strptime("%s/09/30 23:59:59" % (ann_scol,), "%Y/%m/%d %H:%M:%S"))
gtf_fin_periode_transitoire = "%s0930235959+0200" % (__annee,)
# Version timestampées timezone-naïves #Sursis pour les inscription après le 1/11 pour fournir la carte étudiant
debut_periode_transitoire = time.mktime(time.strptime("%s/08/16 00:00:00" % (__annee,), "%Y/%m/%d %H:%M:%S")) sursis_carte=8*24*3600
fin_periode_transitoire = time.mktime(time.strptime("%s/09/30 23:59:59" % (__annee,), "%Y/%m/%d %H:%M:%S"))
# On est en période transitoire si on est dans le bon intervale
periode_transitoire = (debut_periode_transitoire <= time.time() <= fin_periode_transitoire)
ann_scol = __annee
if time.time() <= debut_periode_transitoire:
ann_scol -= 1
# Gel des cableurs pas a jour de cotisation # Gel des cableurs pas a jour de cotisation
# Les droits ne sont pas retires mais il n'y a plus de sudo # Les droits ne sont pas retires mais il n'y a plus de sudo
@ -55,201 +54,89 @@ quota_hard = 10000000
fquota_soft = 0 fquota_soft = 0
fquota_hard = 0 fquota_hard = 0
# Shell # Shell
login_shell = '/bin/zsh' login_shell='/bin/zsh'
club_login_shell = '/usr/bin/rssh' club_login_shell='/usr/bin/rssh'
# Longueur maximale d'un login # Longueur maximale d'un login
maxlen_login = 25 maxlen_login=25
shells_possibles = [ shells_possibles = [u'/bin/csh',
u'/bin/csh', u'/bin/sh', # tout caca
u'/bin/sh', # tout caca u'/bin/dash', # un bash light
u'/bin/dash', # un bash light u'/usr/bin/rc',
u'/usr/bin/rc', u'/usr/bin/ksh', # symlink vers zsh
u'/usr/bin/ksh', # symlink vers zsh u'/bin/ksh', # symlink vers zsh
u'/bin/ksh', # symlink vers zsh u'/usr/bin/tcsh', # TENEX C Shell (csh++)
u'/usr/bin/tcsh', # TENEX C Shell (csh++) u'/bin/tcsh', # TENEX C Shell (csh++)
u'/bin/tcsh', # TENEX C Shell (csh++) u'/bin/bash', # the Bourne-Again SHell
u'/bin/bash', # the Bourne-Again SHell u'/bin/zsh', # the Z shell
u'/bin/zsh', # the Z shell u'/usr/bin/zsh', # the Z shell
u'/usr/bin/zsh', # the Z shell u'/usr/bin/screen',
u'/usr/bin/screen', u'/bin/rbash', # Bash restreint
u'/bin/rbash', # Bash restreint u'/usr/bin/rssh', # restricted secure shell allowing only scp and/or sftp
u'/usr/bin/rssh', # restricted secure shell allowing only scp and/or sftp u'/usr/local/bin/badPassSh', # demande de changer de mot de passe
u'/usr/local/bin/badPassSh', # demande de changer de mot de passe u'/usr/bin/passwd', # idem
u'/usr/bin/passwd', # idem u'/usr/local/bin/disconnect_shell', # déconnexion crans
u'/usr/local/bin/disconnect_shell', # déconnexion crans u'/usr/scripts/surveillance/disconnect_shell', # idem
u'/usr/scripts/surveillance/disconnect_shell', # idem u'/usr/sbin/nologin', # This account is currently not available.
u'/usr/sbin/nologin', # This account is currently not available. u'/bin/false', # vraiement méchant
u'/bin/false', # vraiement méchant u'/usr/bin/es', # n'exsite plus
u'/usr/bin/es', # n'exsite plus u'/usr/bin/esh', # n'existe plus
u'/usr/bin/esh', # n'existe plus u'', # le shell vide pour pouvoir les punis
u'', # le shell vide pour pouvoir les punis
]
shells_gest_crans_order = [
"zsh",
"bash",
"tcsh",
"screen",
"rbash",
"rssh",
"badPassSh",
"disconnect_shell"
] ]
shells_gest_crans_order = ["zsh", "bash", "tcsh", "screen", "rbash", "rssh",
"badPassSh", "disconnect_shell"]
shells_gest_crans = { shells_gest_crans = {
"zsh" : { "zsh": {"path":"/bin/zsh", "desc":"Le Z SHell, shell par defaut sur zamok"},
"path" : "/bin/zsh", "bash": {"path":"/bin/bash", "desc":"Le Boune-Again SHell, shell par defaut de la plupart des linux"},
"desc" : "Le Z SHell, shell par defaut sur zamok" "tcsh": {"path":"/bin/tcsh", "desc":"C SHell ++"},
}, "screen":{"path":'/usr/bin/screen', "desc":"Un gestionnaire de fenêtre dans un terminal"},
"bash" : { "rbash": {"path":"/bin/rbash", "desc":"Un bash très restreint, voir man rbash"},
"path" : "/bin/bash", "rssh": {"path":"/usr/bin/rssh", "desc":"Shell ne permetant que les transferts de fichiers via scp ou sftp"},
"desc" : "Le Boune-Again SHell, shell par defaut de la plupart des linux" "badPassSh":{"path":"/usr/local/bin/badPassSh", "desc":"Demande de changer de mot de passe à la connexion"},
}, "disconnect_shell":{"path":"/usr/local/bin/disconnect_shell", "desc":"Shell pour les suspensions de compte avec message explicatif"},
"tcsh" : {
"path" : "/bin/tcsh",
"desc" : "C SHell ++"
},
"screen" : {
"path" : '/usr/bin/screen',
"desc" : "Un gestionnaire de fenêtre dans un terminal"
},
"rbash" : {
"path" : "/bin/rbash",
"desc" : "Un bash très restreint, voir man rbash"
},
"rssh" : {
"path" : "/usr/bin/rssh",
"desc" : "Shell ne permetant que les transferts de fichiers via scp ou sftp"
},
"badPassSh" : {
"path" : "/usr/local/bin/badPassSh",
"desc" : "Demande de changer de mot de passe à la connexion"
},
"disconnect_shell" : {
"path" : "/usr/local/bin/disconnect_shell",
"desc" : "Shell pour les suspensions de compte avec message explicatif"
},
} }
# Quels droits donnent l'appartenance à quel groupe Unix ? # Quels droits donnent l'appartenance à quel groupe Unix ?
droits_groupes = { droits_groupes = {'adm' : [u'Nounou'],
'adm' : [ 'respbats' : [u'Imprimeur', u'Cableur', u'Nounou'],
u'Nounou', 'apprentis' : [u'Apprenti'],
], 'moderateurs' : [u'Moderateur'],
'respbats' : [ 'disconnect' : [u'Bureau'],
u'Imprimeur', 'imprimeurs' : [u'Imprimeur', u'Nounou', u'Tresorier'],
u'Cableur', 'bureau' : [u'Bureau'],
u'Nounou', 'webadm' : [u'Webmaster'],
], 'webradio' : [u'Webradio'],
'apprentis' : [ }
u'Apprenti',
],
'moderateurs' : [
u'Moderateur',
],
'disconnect' : [
u'Bureau',
],
'imprimeurs' : [
u'Imprimeur',
u'Nounou',
u'Tresorier',
],
'bureau' : [
u'Bureau',
],
'webadm' : [
u'Webmaster',
],
'webradio' : [
u'Webradio',
],
}
####### Les modes de paiement accepté par le crans ####### Les modes de paiement accepté par le crans
modePaiement = [ modePaiement = ['liquide', 'paypal', 'solde', 'cheque', 'carte', 'comnpay', 'arbitraire',]
'liquide',
'paypal',
'solde',
'cheque',
'carte',
'comnpay',
'arbitraire',
'note',
]
####### Les ML ####### Les ML
# Le + devant un nom de ML indique une synchronisation # Le + devant un nom de ML indique une synchronisation
# ML <-> fonction partielle : il n'y a pas d'effacement automatique # ML <-> fonction partielle : il n'y a pas d'effacement automatique
# des abonnés si le droit est retiré # des abonnés si le droit est retiré
droits_mailing_listes = { droits_mailing_listes = {'roots' : [ u'Nounou', u'Apprenti'],
'roots' : [ 'mailman' : [ u'Nounou'],
u'Nounou', '+nounou' : [ u'Nounou', u'Apprenti'],
u'Apprenti', 'respbats' : [ u'Cableur', u'Nounou', u'Bureau'],
], 'moderateurs' : [ u'Moderateur', u'Bureau'],
'mailman' : [ 'disconnect' : [ u'Nounou', u'Bureau'],
u'Nounou', 'impression' : [ u'Imprimeur'],
], 'bureau' : [u'Bureau'],
'+nounou' : [ 'tresorier' : [u'Tresorier'],
u'Nounou', 'apprentis' : [u'Apprenti'],
u'Apprenti', '+ca' : [u'Bureau', u'Apprenti', u'Nounou', u'Cableur'],
],
'respbats' : [
u'Cableur',
u'Nounou',
u'Bureau',
],
'moderateurs' : [
u'Moderateur',
u'Bureau',
],
'disconnect' : [
u'Nounou',
u'Bureau',
],
'impression' : [
u'Imprimeur',
],
'bureau' : [
u'Bureau',
],
'tresorier' : [
u'Tresorier',
],
'apprentis' : [
u'Apprenti',
],
'+ca' : [
u'Bureau',
u'Apprenti',
u'Nounou',
u'Cableur',
],
'+federez' : [
u'Bureau',
u'Apprenti',
u'Nounou',
],
'+install-party' : [
u'Bureau',
u'Apprenti',
u'Nounou',
],
# Correspondance partielle nécessaire... Des adresses non-crans sont inscrites à ces ML. '+federez' : [u'Bureau', u'Apprenti', u'Nounou'],
'+dsi-crans' : [ '+install-party' : [u'Bureau', u'Apprenti', u'Nounou'],
u'Nounou',
u'Bureau', # Correspondance partielle nécessaire... Des adresses non-crans sont inscrites à ces ML.
], '+dsi-crans' : [u'Nounou', u'Bureau'],
'+crous-crans' : [ '+crous-crans' : [u'Nounou', u'Bureau'],
u'Nounou',
u'Bureau', '+wrc' : [u'Webradio'],
], }
'+wrc' : [
u'Webradio',
],
}
#: Répertoire de stockage des objets détruits #: Répertoire de stockage des objets détruits
cimetiere = '/home/cimetiere' cimetiere = '/home/cimetiere'
@ -273,21 +160,21 @@ ISCSI_MAP_FILE = "/usr/scripts/var/iscsi_names_%s.py"
# IANA_id correspond à l'entier attribué par l'IANA pour l'algorithm dans les champs DNS SSHFP # IANA_id correspond à l'entier attribué par l'IANA pour l'algorithm dans les champs DNS SSHFP
# ssh_algo correspond a la première chaine de caractères donnant le nom de l'algorithme de chiffrement lorsque la clef ssh est dans le format openssh (algo key comment) # ssh_algo correspond a la première chaine de caractères donnant le nom de l'algorithme de chiffrement lorsque la clef ssh est dans le format openssh (algo key comment)
sshfp_algo = { sshfp_algo = {
"rsa" : (1, "ssh-rsa"), "rsa" : (1, "ssh-rsa"),
"dsa" : (2, "ssh-dss"), "dsa" : (2, "ssh-dss"),
"ecdsa-256" : (3, "ecdsa-sha2-nistp256"), "ecdsa-256" : (3, "ecdsa-sha2-nistp256"),
"ecdsa-384" : (3, "ecdsa-sha2-nistp384"), "ecdsa-384" : (3, "ecdsa-sha2-nistp384"),
"ecdsa-521" : (3, "ecdsa-sha2-nistp521"), "ecdsa-521" : (3, "ecdsa-sha2-nistp521"),
"ecdsa" : (3, "ecdsa-sha2-nistp521"), "ecdsa" : (3, "ecdsa-sha2-nistp521"),
} }
sshfs_ralgo = {} sshfs_ralgo = {}
for key, value in sshfp_algo.items(): for key, value in sshfp_algo.items():
sshfs_ralgo[value[1]] = (value[0], key) sshfs_ralgo[value[1]] = (value[0], key)
sshfp_hash = { sshfp_hash = {
"sha1" : 1, "sha1" : 1,
"sha256" : 2, "sha256" : 2,
} }
sshkey_max_age = int(9.869604401089358 * (365.25 * 24 * 3600)) sshkey_max_age = int(9.869604401089358 * (365.25 * 24 * 3600))
@ -325,74 +212,40 @@ plage_ens = '138.231.0.0/16'
# clefs qui cassent la bijectivité, mais qui peuvent servir. # clefs qui cassent la bijectivité, mais qui peuvent servir.
# NETs est l'union des deux # NETs est l'union des deux
NETs_primaires = { NETs_primaires = {
'serveurs' : [ 'serveurs' : ['138.231.136.0/24'],
'138.231.136.0/24', 'adherents' : ['138.231.137.0/24', '138.231.138.0/23', '138.231.140.0/22'],
], 'wifi-adh' : ['138.231.144.0/22',
'adherents' : [ '138.231.148.32/27',
'138.231.137.0/24', '138.231.148.64/26',
'138.231.138.0/23', '138.231.148.128/25',
'138.231.140.0/22', '138.231.149.0/24',
], '138.231.150.0/23',
'wifi-adh' : [ ],
'138.231.144.0/22', 'bornes' : [
'138.231.148.32/27', '138.231.148.0/27',
'138.231.148.64/26', ],
'138.231.148.128/25', 'adm' : ['10.231.136.0/24'],
'138.231.149.0/24', 'personnel-ens' : ['10.2.9.0/24'],
'138.231.150.0/23', 'gratuit' : ['10.42.0.0/16'],
], 'accueil' : ['10.51.0.0/16'],
'bornes' : [ 'federez' : ['10.53.0.0/16'],
'138.231.148.0/27', 'isolement' : ['10.52.0.0/16'],
], 'evenementiel' : ['10.231.137.0/24'],
'adm' : [ 'multicast' : ['239.0.0.0/8'],
'10.231.136.0/24' 'ens' : ['138.231.135.0/24'],
], }
'personnel-ens' : [
'10.2.9.0/24'
],
'gratuit' : [
'10.42.0.0/16'
],
'accueil' : [
'10.51.0.0/16'
],
'federez' : [
'10.53.0.0/16'
],
'isolement' : [
'10.52.0.0/16'
],
'evenementiel' : [
'10.231.137.0/24'
],
'multicast' : [
'239.0.0.0/8'
],
'ens' : [
'138.231.135.0/24'
],
}
NETs_secondaires = { NETs_secondaires = {
'all' : [ 'all' : ['138.231.136.0/21', '138.231.144.0/21'],
'138.231.136.0/21', 'wifi': ['138.231.144.0/21'],
'138.231.144.0/21', 'fil' : ['138.231.136.0/21'],
], }
'wifi': [
'138.231.144.0/21',
],
'fil' : [
'138.231.136.0/21',
],
}
NETs = {} NETs = {}
NETs.update(NETs_primaires) NETs.update(NETs_primaires)
NETs.update(NETs_secondaires) NETs.update(NETs_secondaires)
NETs_regexp = { NETs_regexp = { 'all' : '^138\.231\.1(3[6789]|4[0123456789]|5[01])\.\d+$' }
'all' : r'^138\.231\.1(3[6789]|4[0123456789]|5[01])\.\d+$'
}
# Classes de rid # Classes de rid
# Merci d'essayer de les faire correspondre avec les réseaux # Merci d'essayer de les faire correspondre avec les réseaux
@ -429,13 +282,13 @@ rid_primaires = {
'personnel-ens' : [(55296, 55551),], 'personnel-ens' : [(55296, 55551),],
# Un unique rid pour les machines multicast # Un unique rid pour les machines multicast
'multicast' : [(65535, 65535),], 'multicast' : [(65535, 65535),],
} }
rid_secondaires = { rid_secondaires = {
# Rid pour les machines filaire ipv4 # Rid pour les machines filaire ipv4
'fil' : [(0, 2047),], 'fil' : [(0, 2047),],
'wifi' : [(2048, 4095), (34816, 35071),], 'wifi' : [(2048, 4095), (34816, 35071),],
} }
rid = {} rid = {}
rid.update(rid_primaires) rid.update(rid_primaires)
@ -461,59 +314,24 @@ ipv6_machines_speciales = {
} }
# Les préfixes ipv6 publics # Les préfixes ipv6 publics
prefix = { prefix = { 'subnet' : [ '2a01:240:fe3d::/48' ],
'subnet' : [ 'serveurs' : [ '2a01:240:fe3d:4::/64' ],
'2a01:240:fe3d::/48', 'adherents' : [ '2a01:240:fe3d:4::/64' ],
], 'fil' : [ '2a01:240:fe3d:4::/64' ],
'serveurs' : [ 'adm' : [ '2a01:240:fe3d:c804::/64' ],
'2a01:240:fe3d:4::/64', 'adm-v6' : [ '2a01:240:fe3d:c804::/64' ],
], 'wifi' : [ '2a01:240:fe3d:c04::/64' ],
'adherents' : [ 'serveurs-v6' : [ '2a01:240:fe3d:c04::/64' ],
'2a01:240:fe3d:4::/64', 'adherents-v6' : [ '2a01:240:fe3d:4::/64' ],
], 'wifi-adh-v6' : [ '2a01:240:fe3d:c04::/64' ],
'fil' : [ 'personnel-ens' : [ '2a01:240:fe3d:4::/64' ],
'2a01:240:fe3d:4::/64', 'sixxs2' : [ '2a01:240:fe00:68::/64' ],
], 'evenementiel' : [ '2a01:240:fe3d:d2::/64' ],
'adm' : [ 'bornes' : [ '2a01:240:fe3d:c04::/64' ],
'2a01:240:fe3d:c804::/64', 'bornes-v6' : [ '2a01:240:fe3d:c04::/64' ],
], 'wifi-adh' : [ '2a01:240:fe3d:c04::/64' ],
'adm-v6' : [ 'v6only' : [ '2001:470:c8b9:a4::/64' ],
'2a01:240:fe3d:c804::/64', }
],
'wifi' : [
'2a01:240:fe3d:c04::/64',
],
'serveurs-v6' : [
'2a01:240:fe3d:c04::/64',
],
'adherents-v6' : [
'2a01:240:fe3d:4::/64',
],
'wifi-adh-v6' : [
'2a01:240:fe3d:c04::/64',
],
'personnel-ens' : [
'2a01:240:fe3d:4::/64',
],
'sixxs2' : [
'2a01:240:fe00:68::/64',
],
'evenementiel' : [
'2a01:240:fe3d:d2::/64',
],
'bornes' : [
'2a01:240:fe3d:c04::/64',
],
'bornes-v6' : [
'2a01:240:fe3d:c04::/64',
],
'wifi-adh' : [
'2a01:240:fe3d:c04::/64',
],
'v6only' : [
'2001:470:c8b9:a4::/64',
],
}
# Préfixes ipv6 internes (ula) # Préfixes ipv6 internes (ula)
int_prefix = { int_prefix = {
@ -522,12 +340,10 @@ int_prefix = {
} }
# Domaines dans lesquels les machines sont placées suivant leur type # Domaines dans lesquels les machines sont placées suivant leur type
domains = { domains = { 'machineFixe': 'crans.org',
'machineFixe': 'crans.org', 'machineCrans': 'crans.org',
'machineCrans': 'crans.org', 'machineWifi': 'wifi.crans.org',
'machineWifi': 'wifi.crans.org', 'borneWifi': 'wifi.crans.org' }
'borneWifi': 'wifi.crans.org',
}
# VLans # VLans
vlans = { vlans = {
@ -547,6 +363,8 @@ vlans = {
'v6only': 6, 'v6only': 6,
# Vlan isolement # Vlan isolement
'isolement' : 9, 'isolement' : 9,
# Vlan de tests de chiffrement DSI
'chiffrement': 11,
# VLan des appartements de l'ENS # VLan des appartements de l'ENS
'appts': 21, 'appts': 21,
# Vlan federez-wifi # Vlan federez-wifi
@ -559,45 +377,33 @@ vlans = {
'iscsi': 42, 'iscsi': 42,
# freebox (pour faire descendre la connexion au 0B) # freebox (pour faire descendre la connexion au 0B)
'freebox': 8, 'freebox': 8,
}
filter_policy = {
'komaz' : {
'policy_input' : 'ACCEPT',
'policy_forward' : 'ACCEPT',
'policy_output' : 'ACCEPT',
},
'zamok' : {
'policy_input' : 'ACCEPT',
'policy_forward' : 'DROP',
'policy_output' : 'ACCEPT',
},
'default' : {
'policy_input' : 'ACCEPT',
'policy_forward' : 'ACCEPT',
'policy_output' : 'ACCEPT',
} }
}
filter_policy = { 'komaz' : { 'policy_input' : 'ACCEPT',
'policy_forward' : 'ACCEPT',
'policy_output' : 'ACCEPT'
},
'zamok' : { 'policy_input' : 'ACCEPT',
'policy_forward' : 'DROP',
'policy_output' : 'ACCEPT'
},
'default' : { 'policy_input' : 'ACCEPT',
'policy_forward' : 'ACCEPT',
'policy_output' : 'ACCEPT'
}
}
# Cf RFC 4890 # Cf RFC 4890
authorized_icmpv6 = [ authorized_icmpv6 = ['echo-request', 'echo-reply', 'destination-unreachable',
'echo-request', 'packet-too-big', 'ttl-zero-during-transit', 'parameter-problem']
'echo-reply',
'destination-unreachable',
'packet-too-big',
'ttl-zero-during-transit',
'parameter-problem',
]
output_file = { output_file = { 4 : '/tmp/ipt_rules',
4 : '/tmp/ipt_rules', 6 : '/tmp/ip6t_rules'
6 : '/tmp/ip6t_rules', }
}
file_pickle = { file_pickle = { 4 : '/tmp/ipt_pickle',
4 : '/tmp/ipt_pickle', 6 : '/tmp/ip6t_pickle'
6 : '/tmp/ip6t_pickle', }
}
################################################################################## ##################################################################################
#: Items de la blackliste #: Items de la blackliste
@ -621,7 +427,7 @@ blacklist_sanctions = [
] ]
#: Blacklistes redirigeant le port 80 en http vers le portail captif (avec des explications) #: Blacklistes redirigeant le port 80 en http vers le portail captif (avec des explications)
blacklist_sanctions_soft = [ blacklist_sanctions_soft = [
'ipv6_ra', 'ipv6_ra',
'mail_invalide', 'mail_invalide',
'virus', 'virus',
@ -635,24 +441,10 @@ blacklist_bridage_upload = ['autodisc_upload', 'upload']
################################################################################## ##################################################################################
adm_users = [ adm_users = [ 'root', 'identd', 'daemon', 'postfix', 'freerad', 'amavis',
'root', 'nut', 'respbats', 'list', 'sqlgrey', 'ntpd', 'lp' ]
'identd',
'daemon',
'postfix',
'freerad',
'amavis',
'nut',
'respbats',
'list',
'sqlgrey',
'ntpd',
'lp',
]
open_ports = { open_ports = { 'tcp' : '22' }
'tcp' : '22',
}
# Debit max sur le vlan de la connexion gratuite # Debit max sur le vlan de la connexion gratuite
debit_max_radin = 1000000 debit_max_radin = 1000000
@ -662,83 +454,13 @@ debit_max_gratuit = 1000000
## Vlan accueil et isolement ## ## Vlan accueil et isolement ##
############################### ###############################
accueil_route = { accueil_route = {
'138.231.136.1' : { '138.231.136.1':{'tcp':['80','443', '22'],'hosts':['intranet.crans.org', 'ssh.crans.org', 'zamok.crans.org']},
'tcp' : [ '138.231.136.67':{'tcp':['80','443'],'hosts':['www.crans.org', 'wiki.crans.org', 'wifi.crans.org']},
'80', '138.231.136.98':{'tcp':['20','21','80','111','1024:65535'],'udp':['69','1024:65535'], 'hosts':['ftp.crans.org']},
'443', '138.231.136.130':{'tcp':['80','443'],'hosts':['intranet2.crans.org']},
'22' '138.231.136.18':{'tcp':['80','443'],'hosts':['cas.crans.org', 'login.crans.org', 'auth.crans.org']},
], '213.154.225.236':{'tcp':['80','443'], 'hosts':['crl.cacert.org']},
'hosts' : [ '213.154.225.237':{'tcp':['80','443'], 'hosts':['ocsp.cacert.org']},
'ssh.crans.org',
'zamok.crans.org',
],
},
'138.231.136.67' : {
'tcp' : [
'80',
'443',
],
'hosts' : [
'www.crans.org',
'wiki.crans.org',
'wifi.crans.org',
],
},
'138.231.136.98' : {
'tcp' : [
'20',
'21',
'80',
'111',
'1024:65535',
],
'udp' : [
'69',
'1024:65535',
],
'hosts' : [
'ftp.crans.org',
],
},
'138.231.136.130' : {
'tcp' : [
'80',
'443',
],
'hosts' : [
'intranet2.crans.org',
'intranet.crans.org',
],
},
'138.231.136.18' : {
'tcp' : [
'80',
'443',
],
'hosts' : [
'cas.crans.org',
'login.crans.org',
'auth.crans.org',
],
},
'213.154.225.236' : {
'tcp' : [
'80',
'443',
],
'hosts' : [
'crl.cacert.org',
],
},
'213.154.225.237' : {
'tcp' : [
'80',
'443',
],
'hosts' : [
'ocsp.cacert.org',
],
},
} }
dhcp_servers = ['dhcp.adm.crans.org', 'isc.adm.crans.org'] dhcp_servers = ['dhcp.adm.crans.org', 'isc.adm.crans.org']

View file

@ -10,17 +10,11 @@
# Délai minimal avant de pouvoir réadhérer. # Délai minimal avant de pouvoir réadhérer.
# Ne tient pas compte de la période transitoire, qui est un confort # Ne tient pas compte de la période transitoire, qui est un confort
# pour l'administration. # pour l'administration.
delai_readh_jour = 32 delai_readh_jour = 28
delai_readh = delai_readh_jour * 86400 delai_readh = delai_readh_jour * 86400
duree_adh_an = 1 duree_adh_an = 1
# Un compte avec une adhésion valide ne peut être détruit que lorsque celle-ci
# est expirée depuis plus que le délai indiqué ici. (secondes)
# Ici, on choisit 90 jours.
del_post_adh_jours = 90
del_post_adh = del_post_adh_jours * 86400
# Cotisation pour adhérer à l'association. Les services autres que l'accès à # Cotisation pour adhérer à l'association. Les services autres que l'accès à
# Internet sont offerts une et une fois pour toute aux personnes qui adhèrent, # Internet sont offerts une et une fois pour toute aux personnes qui adhèrent,
# et ce dès leur première fois. (comprendre : le compte Crans et cie ne sont pas # et ce dès leur première fois. (comprendre : le compte Crans et cie ne sont pas

View file

@ -130,14 +130,6 @@ recursiv = {
], ],
} }
#: Domaines correspondant à des mails crans
mail_crans = [
'crans.org',
'crans.fr',
'crans.eu',
'crans.ens-cachan.fr',
]
#: Les ip/net des vlans limité vue par les récursifs #: Les ip/net des vlans limité vue par les récursifs
menteur_clients = [ menteur_clients = [
"138.231.136.210", "138.231.136.210",

View file

@ -4,4 +4,3 @@ import sys
in_encoding = getattr(sys.stdin, 'encoding', None) or "UTF-8" in_encoding = getattr(sys.stdin, 'encoding', None) or "UTF-8"
out_encoding = getattr(sys.stdout, 'encoding', None) or "UTF-8" out_encoding = getattr(sys.stdout, 'encoding', None) or "UTF-8"
ldap_encoding = "UTF-8"

View file

@ -12,14 +12,10 @@ ITEMS = {
'designation': u'Cable Ethernet 5m', 'designation': u'Cable Ethernet 5m',
'pu': 3., 'pu': 3.,
}, },
'ADAPTATEUR_TrendNet': { 'ADAPTATEUR': {
'designation': u'Adaptateur 10/100 Ethernet/USB-2', 'designation': u'Adaptateur Ethernet/USB',
'pu': 17., 'pu': 17.,
}, },
'ADAPTATEUR_UGreen': {
'designation': u'Adaptateur 10/100/1000 Ethernet/USB-3',
'pu': 14.,
},
'RELIURE': { 'RELIURE': {
'designation': u'Reliure plastique', 'designation': u'Reliure plastique',
'pu': 0.12, 'pu': 0.12,
@ -41,27 +37,3 @@ ITEMS = {
'pu': 28.92, 'pu': 28.92,
}, },
} }
# Utilisé par gest_crans_lc, contient également le rachargement de solde
ITEM_SOLDE = {'SOLDE': {'designation': u'Rechargement de solde', 'pu': u'*'}}
# Dico avec les modes de paiement pour modification du solde
SOLDE = {
'liquide' : u'Espèces',
'cheque' : u'Chèque',
'carte': u'Carte bancaire',
'note': u'Note Kfet',
'arbitraire': u'Modification arbitraire du solde',
}
# Dico avec les modes de paiement pour la vente
VENTE = {
'liquide' : u'Espèces',
'cheque' : u'Chèque',
'carte': u'Carte bancaire',
'note': u'Note Kfet',
'solde': u'Vente à partir du Solde',
}

View file

@ -53,20 +53,12 @@ mask = [24]
now=datetime.datetime.now() now=datetime.datetime.now()
if now.hour >= 6 and now.hour < 19 and now.weekday() < 5 and not is_ferie(): if now.hour >= 6 and now.hour < 19 and now.weekday() < 5 and not is_ferie():
#: Débit maximal autorisé #: Débit maximal autorisé
debit_max = { 'total' : 250, debit_max = 150 # mbits per second en connexion de jour
'out' : 250,
'wifi' : 100,
'fil' : 150 }
# mbits per second en connexion de jour
#: Est-ce qu'on est en connexion de jour ou de nuit/week-end ? #: Est-ce qu'on est en connexion de jour ou de nuit/week-end ?
debit_jour = True debit_jour = True
else: else:
#: Débit maximal autorisé #: Débit maximal autorisé
debit_max = { 'total' : 600, debit_max = 500 # mbits per second en conn de nuit et du week-end
'out' : 600,
'wifi' : 150,
'fil' : 450 }
# mbits per second en conn de nuit et du week-end
#: Est-ce qu'on est en connexion de jour ou de nuit/week-end ? #: Est-ce qu'on est en connexion de jour ou de nuit/week-end ?
debit_jour = False debit_jour = False
@ -81,7 +73,7 @@ federez_upload_max = 10 #mbytes per second
# Debit appartement down max # Debit appartement down max
# TODO : mettre en place dans komaz.py # TODO : mettre en place dans komaz.py
appt_download_max = debit_max['total']/10 appt_download_max = debit_max/10
#: Liste des réseaux non routables #: Liste des réseaux non routables
reseaux_non_routables = [ '10.0.0.0/8', '172.16.0.0/12','198.18.0.0/15', reseaux_non_routables = [ '10.0.0.0/8', '172.16.0.0/12','198.18.0.0/15',

View file

@ -7,7 +7,7 @@
import itertools import itertools
import os import os
debug = (int(os.environ.get('DBG_TRIGGER', 0)) == 1) or True debug = (int(os.environ['DBG_TRIGGER']) == 1) or True
log_level = "info" log_level = "info"
# Serveur maître # Serveur maître
@ -16,12 +16,6 @@ user = "trigger"
port = 5671 port = 5671
ssl = True ssl = True
# TTL en secondes pour les messages en attente.
# Une suite d'opérations a faire a un ob_id, qui est un hash.
# Quand cette suite traîne depuis trop longtemps en attente sans que rien
# ne se passe, on la jette.
MSG_TTL = 3600
# Liste des services associés aux hôtes # Liste des services associés aux hôtes
# useradd : Envoie le mail de bienvenue, et crée le home # useradd : Envoie le mail de bienvenue, et crée le home
# userdel : Détruit le home, déconnecte l'utilisateur sur zamok, détruit les indexes dovecot, désinscrit l'adresse crans des mailing listes associées # userdel : Détruit le home, déconnecte l'utilisateur sur zamok, détruit les indexes dovecot, désinscrit l'adresse crans des mailing listes associées

View file

@ -19,7 +19,7 @@ if '/usr/scripts' not in sys.path:
from pythondialog import Dialog as PythonDialog from pythondialog import Dialog as PythonDialog
from pythondialog import DialogTerminatedBySignal, PythonDialogErrorBeforeExecInChildProcess from pythondialog import DialogTerminatedBySignal, PythonDialogErrorBeforeExecInChildProcess
from pythondialog import error as DialogError from pythondialog import error as DialogError
from gestion import affichage from gestion.affich_tools import get_screen_size, coul
debug_enable = False debug_enable = False
debugf = None debugf = None
@ -203,7 +203,7 @@ class Dialog(object):
setattr(self, attr, ret) setattr(self, attr, ret)
return ret return ret
def __init__(self, debug_enable=False, dialogrc=False): def __init__(self, debug_enable=False):
signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN)
self.debug_enable = debug_enable self.debug_enable = debug_enable
@ -211,7 +211,6 @@ class Dialog(object):
# On met un timeout à 10min d'innactivité sur dialog # On met un timeout à 10min d'innactivité sur dialog
self.timeout = 600 self.timeout = 600
self.error_to_raise = (Continue, DialogError, ldap.SERVER_DOWN) self.error_to_raise = (Continue, DialogError, ldap.SERVER_DOWN)
self.dialogrc = dialogrc
_dialog = None _dialog = None
@property @property
@ -219,9 +218,7 @@ class Dialog(object):
""" """
Renvois l'objet dialog. Renvois l'objet dialog.
""" """
if self.dialogrc: if self._dialog is None:
self._dialog = PythonDialog(DIALOGRC=self.dialogrc)
else:
self._dialog = PythonDialog() self._dialog = PythonDialog()
self.dialog_last_access = time.time() self.dialog_last_access = time.time()
return self._dialog return self._dialog
@ -230,7 +227,7 @@ class Dialog(object):
""" """
Nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan Nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan nyan
""" """
(cols, lines) = affichage.getTerminalSize() (lines, cols) = get_screen_size()
print "\033[48;5;17m" print "\033[48;5;17m"
print " "*(lines * cols) print " "*(lines * cols)
cols = int(min(cols/2, 65)) cols = int(min(cols/2, 65))

View file

@ -9,8 +9,6 @@ Licence : GPLv3
import sys import sys
import time import time
import datetime import datetime
import subprocess
import pytz
import dateutil.relativedelta import dateutil.relativedelta
if '/usr/scripts' not in sys.path: if '/usr/scripts' not in sys.path:
sys.path.append('/usr/scripts') sys.path.append('/usr/scripts')
@ -20,7 +18,6 @@ import config.cotisation
import lc_ldap.objets as objets import lc_ldap.objets as objets
import lc_ldap.attributs as attributs import lc_ldap.attributs as attributs
from lc_ldap.attributs import UniquenessError from lc_ldap.attributs import UniquenessError
from lc_ldap import crans_utils
import proprio import proprio
from CPS import TailCall, tailcaller, Continue from CPS import TailCall, tailcaller, Continue
@ -48,20 +45,20 @@ class Dialog(proprio.Dialog):
'GPGFingerprint' : [a.nounou, a.soi], 'GPGFingerprint' : [a.nounou, a.soi],
'Remarques' : [a.cableur, a.nounou], 'Remarques' : [a.cableur, a.nounou],
'Droits':[a.nounou, a.bureau], 'Droits':[a.nounou, a.bureau],
'Blackliste':[a.bureau, a.nounou], 'Blackliste':[a.cableur, a.nounou],
'Vente':[a.cableur, a.nounou], 'Vente':[a.cableur, a.nounou],
'Supprimer':[a.nounou, a.bureau], 'Supprimer':[a.nounou, a.bureau],
} }
menu = { menu = {
'Administratif' : {'text' : "Adhésion, chartes", "callback":self.adherent_administratif}, 'Administratif' : {'text' : "Adhésion, carte étudiant, chartes", "callback":self.adherent_administratif},
'Personnel' : {'text' : "Nom, prénom, téléphone, et mail de contact", 'callback':self.adherent_personnel}, 'Personnel' : {'text' : "Nom, prénom, téléphone... (ajouter l'age ?)", 'callback':self.adherent_personnel},
'Études' : {'text' : "Étude en cours", "callback":self.adherent_etudes}, 'Études' : {'text' : "Étude en cours", "callback":self.adherent_etudes},
'Chambre' : {'text' : 'Déménagement', "callback":self.adherent_chambre}, 'Chambre' : {'text' : 'Déménagement', "callback":self.adherent_chambre},
'Compte' : {'text' : "Gestion du compte crans", "adherent":"proprio", "callback":TailCall(self.proprio_compte, update_obj='adherent'), 'help':"Création/Suppression/Activation/Désactivation du compte, gestion des alias mails crans du compte"}, 'Compte' : {'text' : "Gestion du compte crans", "adherent":"proprio", "callback":TailCall(self.proprio_compte, update_obj='adherent'), 'help':"Création/Suppression/Activation/Désactivation du compte, gestion des alias mails crans du compte"},
'GPGFingerprint' : {'text':'Ajouter ou supprimer une empeinte GPG', 'attribut':attributs.gpgFingerprint}, 'GPGFingerprint' : {'text':'Ajouter ou supprimer une empeinte GPG', 'attribut':attributs.gpgFingerprint},
'Remarques' : {'text':'Ajouter ou supprimer une remarque à cet adhérent', 'attribut':attributs.info}, 'Remarques' : {'text':'Ajouter ou supprimer une remarque de la machine', 'attribut':attributs.info},
'Droits' : {'text':"Modifier les droits alloués à cet adhérent", "callback":self.adherent_droits}, 'Droits' : {'text':"Modifier les droits alloués à cet adhérent", "callback":self.adherent_droits},
'Blackliste' : {'text': 'Modifier les blacklist de cet adhérent', 'callback':self.modif_adherent_blacklist}, 'Blackliste' : {'text': 'Modifier les blacklist de la machine', 'callback':self.modif_adherent_blacklist},
'Vente' : {'text':"Chargement solde crans, vente de cable ou adaptateur ethernet ou autre", "adherent":"proprio", "callback":self.proprio_vente}, 'Vente' : {'text':"Chargement solde crans, vente de cable ou adaptateur ethernet ou autre", "adherent":"proprio", "callback":self.proprio_vente},
'Supprimer' : {'text':"Supprimer l'adhérent de la base de donnée", 'callback':TailCall(self.delete_adherent, del_cont=cont(proprio=None))}, 'Supprimer' : {'text':"Supprimer l'adhérent de la base de donnée", 'callback':TailCall(self.delete_adherent, del_cont=cont(proprio=None))},
} }
@ -126,13 +123,17 @@ class Dialog(proprio.Dialog):
"Adhésion": [a.cableur, a.nounou], "Adhésion": [a.cableur, a.nounou],
'Connexion': [a.cableur, a.nounou], 'Connexion': [a.cableur, a.nounou],
"Charte MA" : [a.nounou, a.bureau], "Charte MA" : [a.nounou, a.bureau],
"Carte Étudiant" : [a.nounou, a.cableur, a.tresorier],
} }
menu = { menu = {
"Adhésion" : {"text":"Pour toute réadhésion *sans* connexion.", "help":"", "callback":self.adherent_adhesion}, "Adhésion" : {"text":"Pour toute réadhésion *sans* connexion.", "help":"", "callback":self.adherent_adhesion},
'Connexion' : {'text': "Mise à jour de l'accès Internet (effectue la réadhésion si besoin)", "help":"", 'callback':self.adherent_connexion}, 'Connexion' : {'text': "Mise à jour de l'accès Internet (effectue la réadhésion si besoin)", "help":"", 'callback':self.adherent_connexion},
"Carte Étudiant" : {"text" : "Validation de la carte étudiant", "help":"", "callback":self.adherent_carte_etudiant},
"Charte MA" : {"text" : "Signature de la charte des membres actifs", "help":'', "callback":self.adherent_charte}, "Charte MA" : {"text" : "Signature de la charte des membres actifs", "help":'', "callback":self.adherent_charte},
} }
menu_order = ["Adhésion", 'Connexion'] menu_order = ["Adhésion", 'Connexion']
if self.has_right(a.tresorier, adherent) or not adherent.carte_controle():
menu_order.append("Carte Étudiant")
menu_order.append("Charte MA") menu_order.append("Charte MA")
def box(default_item=None): def box(default_item=None):
return self.dialog.menu( return self.dialog.menu(
@ -220,7 +221,8 @@ class Dialog(proprio.Dialog):
# Boite si on ne peux pas réahdérer # Boite si on ne peux pas réahdérer
def box_already(end): def box_already(end):
self.dialog.msgbox("Actuellement adhérent jusqu'au %s.\nMerci de revenir lorsqu'il restera moins de %s jours avant la fin." % (end, config.cotisation.delai_readh_jour), t_end = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(end))
self.dialog.msgbox("Actuellement adhérent jusqu'au %s.\nMerci de revenir lorsqu'il restera moins de %s jours avant la fin." % (t_end, config.cotisation.delai_readh_jour),
width=0, width=0,
height=0, height=0,
timeout=self.timeout, timeout=self.timeout,
@ -228,8 +230,9 @@ class Dialog(proprio.Dialog):
# Boite de confirmation à l'ahésion # Boite de confirmation à l'ahésion
def box_adherer(end=None): def box_adherer(end=None):
if end != crans_utils.localized_datetime(): if end:
adherer = self.confirm(text="Adhésion jusqu'au %s. Réadhérer ?" % end, title="Adhésion de %s %s" % (adherent.get("prenom", [''])[0], adherent["nom"][0])) t_end = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(end))
adherer = self.confirm(text="Adhésion jusqu'au %s. Réadhérer ?" % t_end, title="Adhésion de %s %s" % (adherent.get("prenom", [''])[0], adherent["nom"][0]))
else: else:
adherer = self.confirm(text="Adhésion pour un an, continuer ?", title="Adhésion de %s %s" % (adherent.get("prenom", [''])[0], adherent["nom"][0])) adherer = self.confirm(text="Adhésion pour un an, continuer ?", title="Adhésion de %s %s" % (adherent.get("prenom", [''])[0], adherent["nom"][0]))
return adherer return adherer
@ -243,8 +246,9 @@ class Dialog(proprio.Dialog):
# Génération de la facture pour adhésion # Génération de la facture pour adhésion
def paiement(tag_paiement, adherent, finadhesion, comment, facture, cancel_cont, cont): def paiement(tag_paiement, adherent, finadhesion, comment, facture, cancel_cont, cont):
now = crans_utils.localized_datetime() now = time.time()
new_finadhesion = max(finadhesion, now).replace(year=max(finadhesion, now).year + 1) new_finadhesion = datetime.datetime.fromtimestamp(max(finadhesion, now))
new_finadhesion = time.mktime(new_finadhesion.replace(year=new_finadhesion.year + config.cotisation.duree_adh_an).timetuple()) + 86400
new_debutadhesion = now new_debutadhesion = now
if facture: if facture:
facture = self.conn.search(dn=facture.dn, scope=0, mode='rw')[0] facture = self.conn.search(dn=facture.dn, scope=0, mode='rw')[0]
@ -256,8 +260,8 @@ class Dialog(proprio.Dialog):
facture['modePaiement'] = unicode(tag_paiement, 'utf-8') facture['modePaiement'] = unicode(tag_paiement, 'utf-8')
facture['info'] = unicode(comment, 'utf-8') facture['info'] = unicode(comment, 'utf-8')
facture['article'].append(config.cotisation.dico_adh) facture['article'].append(config.cotisation.dico_adh)
facture["finAdhesion"] = new_finadhesion facture["finAdhesion"] = unicode(new_finadhesion)
facture["debutAdhesion"] = new_debutadhesion facture["debutAdhesion"] = unicode(new_debutadhesion)
# On peut retarder le credit pour ajouter des contribution pour la connexion internet à la facture # On peut retarder le credit pour ajouter des contribution pour la connexion internet à la facture
if crediter: if crediter:
if self.confirm_item(item=facture, if self.confirm_item(item=facture,
@ -280,13 +284,9 @@ class Dialog(proprio.Dialog):
raise Continue(cont(adherent=adherent)) raise Continue(cont(adherent=adherent))
now = crans_utils.localized_datetime() finadhesion = adherent.fin_adhesion()
try:
finadhesion = adherent.fin_adhesion().value
except AttributeError:
finadhesion = now
# Si fin de l'adhésion trop loin dans le futur, rien a faire # Si fin de l'adhésion trop loin dans le futur, rien a faire
if finadhesion and (finadhesion - now).days > config.cotisation.delai_readh_jour: if finadhesion and finadhesion - config.cotisation.delai_readh > time.time():
self.handle_dialog(cancel_cont if cancel_cont else cont, box_already, finadhesion) self.handle_dialog(cancel_cont if cancel_cont else cont, box_already, finadhesion)
raise Continue(cancel_cont if cancel_cont else cont) raise Continue(cancel_cont if cancel_cont else cont)
@ -340,9 +340,9 @@ class Dialog(proprio.Dialog):
# Une boite pour choisir un nombre de mois pour prolonger la connexion # Une boite pour choisir un nombre de mois pour prolonger la connexion
def box(finconnexion, default_item=None): def box(finconnexion, default_item=None):
t_end = finconnexion t_end = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(finconnexion))
return self.dialog.menu( return self.dialog.menu(
"Connexion jusqu'au %s" % t_end if finconnexion != datetime.datetime.fromtimestamp(0, tz=pytz.utc) else "N'a jamais été connecté", "Connexion jusqu'au %s" % t_end if finconnexion else "N'a jamais été connecté",
width=0, width=0,
height=0, height=0,
menu_height=0, menu_height=0,
@ -357,16 +357,17 @@ class Dialog(proprio.Dialog):
# Génération et crédit de la facture # Génération et crédit de la facture
def todo(adherent, mois, finadhesion, finconnexion, cancel_cont, cont, facture=None, tag_paiment=None, comment=None): def todo(adherent, mois, finadhesion, finconnexion, cancel_cont, cont, facture=None, tag_paiment=None, comment=None):
now = crans_utils.localized_datetime() now = time.time()
new_finconnexion = datetime.datetime.fromtimestamp(max(finconnexion, now))
# On ajoute 3600 secondes sur suggestion de Raphaël Bonaque (<bonaque@crans.org>), pour tenir compte des malheureux qui
# pourraient subir le changement d'heure.
new_finconnexion = time.mktime((new_finconnexion + dateutil.relativedelta.relativedelta(months=mois)).timetuple()) + 3600
new_debutconnexion = max(now, finconnexion) new_debutconnexion = max(now, finconnexion)
con_month = new_debutconnexion.month
con_year = new_debutconnexion.year
new_finconnexion = max(finconnexion, now).replace(year=con_year + ((con_month + mois) // 13), month= (con_month + mois - 1) % 12 + 1)
if (new_finconnexion - finadhesion.value).days > 0: if new_finconnexion > finadhesion:
t_end_adh = finadhesion.value t_end_adh = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(finadhesion))
t_end_conn = finconnexion t_end_conn = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(new_finconnexion))
if (new_finconnexion - finadhesion.value).days > 30: if new_finconnexion - finadhesion > 30 * 3600 * 24:
raise ValueError("Impossible de prolonger la connexion jusqu'au %s plus d'un mois après la fin de l'adhésion au %s" % (t_end_conn, t_end_adh)) raise ValueError("Impossible de prolonger la connexion jusqu'au %s plus d'un mois après la fin de l'adhésion au %s" % (t_end_conn, t_end_adh))
else: else:
if not self.confirm("La fin de la connexion de l'adhérent (%s) tombera après la fin de son adhésion (%s).\n" \ if not self.confirm("La fin de la connexion de l'adhérent (%s) tombera après la fin de son adhésion (%s).\n" \
@ -376,8 +377,8 @@ class Dialog(proprio.Dialog):
if facture: if facture:
with self.conn.search(dn=facture.dn, scope=0, mode='rw')[0] as facture: with self.conn.search(dn=facture.dn, scope=0, mode='rw')[0] as facture:
if mois: if mois:
facture["finConnexion"] = new_finconnexion facture["finConnexion"] = unicode(new_finconnexion)
facture["debutConnexion"] = new_debutconnexion facture["debutConnexion"] = unicode(new_debutconnexion)
facture["article"].append(config.cotisation.dico_cotis(mois)) facture["article"].append(config.cotisation.dico_cotis(mois))
if self.confirm_item(item=facture, if self.confirm_item(item=facture,
text=u"Le paiement de %sEUR a-t-il bien été reçu (mode : %s) ?\n" % (facture.total(), facture['modePaiement'][0]), text=u"Le paiement de %sEUR a-t-il bien été reçu (mode : %s) ?\n" % (facture.total(), facture['modePaiement'][0]),
@ -400,8 +401,8 @@ class Dialog(proprio.Dialog):
facture['modePaiement'] = unicode(tag_paiment, 'utf-8') facture['modePaiement'] = unicode(tag_paiment, 'utf-8')
facture['article'].append(config.cotisation.dico_cotis(mois)) facture['article'].append(config.cotisation.dico_cotis(mois))
facture['info'] = unicode(comment, 'utf-8') facture['info'] = unicode(comment, 'utf-8')
facture["finConnexion"] = new_finconnexion facture["finConnexion"] = unicode(new_finconnexion)
facture["debutConnexion"] = new_debutconnexion facture["debutConnexion"] = unicode(new_debutconnexion)
if self.confirm_item(item=facture, if self.confirm_item(item=facture,
text=u"Le paiement de %sEUR a-t-il bien été reçu (mode : %s) ?\n" % (facture.total(), tag_paiment), text=u"Le paiement de %sEUR a-t-il bien été reçu (mode : %s) ?\n" % (facture.total(), tag_paiment),
title=u"Validation du paiement", title=u"Validation du paiement",
@ -411,7 +412,7 @@ class Dialog(proprio.Dialog):
else: else:
if not self.confirm(text=u"Le paiement n'a pas été reçue.\n Annuler ?", title="Annulation de l'adhésion", defaultno=True): if not self.confirm(text=u"Le paiement n'a pas été reçue.\n Annuler ?", title="Annulation de l'adhésion", defaultno=True):
raise Continue(cancel_cont) raise Continue(cancel_cont)
raise Continue(cont) raise Continue(cont(adherent=adherent))
def todo_mois(tag, self_cont): def todo_mois(tag, self_cont):
if tag == 'An': if tag == 'An':
@ -433,18 +434,16 @@ class Dialog(proprio.Dialog):
finconnexion = adherent.fin_connexion() finconnexion = adherent.fin_connexion()
# Si l'adhésion fini avant la connexion # Si l'adhésion fini avant la connexion
if finadhesion <= crans_utils.localized_datetime() or finadhesion <= finconnexion: if finadhesion <= time.time() or finadhesion <= finconnexion:
if finadhesion: if finadhesion:
t_end_adh = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(finadhesion))
# Si l'adhésion est déjà fini # Si l'adhésion est déjà fini
if finadhesion <= crans_utils.localized_datetime(): if finadhesion <= time.time():
if finadhesion == datetime.datetime.fromtimestamp(0, tz=pytz.utc): self.dialog.msgbox(text=u"L'adhésion a expiré le %s, il va falloir réadhérer d'abord" % t_end_adh, title="Réadhésion nécessaire", width=0, height=0, timeout=self.timeout)
self.dialog.msgbox(text=u"L'adhérent n'a jamais adhéré à l'association, on va d'abord le faire adhérer (10€)", title="Adhésion nécessaire", width=0, height=0, timeout=self.timeout)
else:
self.dialog.msgbox(text=u"L'adhésion a expiré le %s, il va falloir réadhérer d'abord (10€)" % finadhesion, title="Réadhésion nécessaire", width=0, height=0, timeout=self.timeout)
# Sinon si elle fini avant la fin de la connexion courante # Sinon si elle fini avant la fin de la connexion courante
elif finadhesion < finconnexion: elif finadhesion < finconnexion:
t_end_conn = finconnexion t_end_conn = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(finconnexion))
self.dialog.msgbox(text=u"L'adhésion de termine le %s, avant la fin de la connexion le %s, il va falloir réadhérer d'abord (10€)" % (finadhesion, t_end_conn), title="Réadhésion nécessaire", width=0, height=0, timeout=self.timeout) self.dialog.msgbox(text=u"L'adhésion de termine le %s, avant la fin de la connexion le %s, il va falloir réadhérer d'abord" % (t_end_adh, t_end_conn), title="Réadhésion nécessaire", width=0, height=0, timeout=self.timeout)
# Échouera si on essaie de prolonger la connexion au dela de l'adhésion et que l'adhésion est encore valable plus de quinze jours # Échouera si on essaie de prolonger la connexion au dela de l'adhésion et que l'adhésion est encore valable plus de quinze jours
return self.adherent_adhesion(cont=self_cont, cancel_cont=cont, adherent=adherent, crediter=False) return self.adherent_adhesion(cont=self_cont, cancel_cont=cont, adherent=adherent, crediter=False)
@ -484,6 +483,76 @@ class Dialog(proprio.Dialog):
return self.proprio_choose_paiement(proprio=adherent, cont=self_cont, cancel_cont=lcont) return self.proprio_choose_paiement(proprio=adherent, cont=self_cont, cancel_cont=lcont)
return cont return cont
def adherent_carte_etudiant(self, cont, adherent, values={}, cancel_cont=None):
# Dictionnaire décrivant quelle est la valeur booléenne à donner à l'absence de l'attribut
a = attributs
choices = []
if self.has_right(a.tresorier, adherent) or not adherent.carte_controle():
choices.append((a.carteEtudiant.ldap_name, "Carte étudiant présentée", 1 if adherent[a.carteEtudiant.ldap_name] or values.get(a.carteEtudiant.ldap_name, False) else 0))
if self.has_right(a.tresorier, adherent):
choices.append(("controleCarte", "La carte a-t-elle été controlée", 1 if adherent.carte_controle() or values.get("controleCarte", False) else 0))
if not choices:
self.dialog.msgbox("Carte d'étudiant déjà validée et non modifiable", title="Gestion de la carte étudiant", width=0, height=0)
if cancel_cont:
cancel_cont(cont=cont)
try:
cont(cancel_cont=cancel_cont)
except TypeError:
pass
raise Continue(cont)
def box():
return self.dialog.checklist("Gestion de la carte étudiant",
height=0,
width=0,
timeout=self.timeout,
list_height=7,
choices=choices,
title="Gestion de la carte étudiant")
def todo(values, adherent, cont):
# On met à jour chaque attribut si sa valeur à changé
with self.conn.search(dn=adherent.dn, scope=0, mode='rw')[0] as adherent:
# Si on est trésorier et que controleCarte a changer on enregistre le changement
if self.has_right(a.tresorier, adherent) and values["controleCarte"] and not adherent.carte_controle():
if adherent["controle"]:
adherent["controle"] = u"c%s" % adherent["controle"][0]
else:
adherent["controle"] = u"c"
elif self.has_right(a.tresorier, adherent) and not values["controleCarte"] and adherent.carte_controle():
adherent["controle"] = unicode(adherent["controle"][0]).replace('c','')
if not adherent["controle"][0]:
adherent["controle"] = []
# Si la carte n'est pas validé ou qu'on est trésorier, on sauvegarde les changements
if values[a.carteEtudiant.ldap_name] and not adherent[a.carteEtudiant.ldap_name] and (not adherent.carte_controle() or self.has_right(a.tresorier, adherent)):
adherent[a.carteEtudiant.ldap_name] = u"TRUE"
elif not values[a.carteEtudiant.ldap_name] and adherent[a.carteEtudiant.ldap_name] and (not adherent.carte_controle() or self.has_right(a.tresorier, adherent)):
adherent[a.carteEtudiant.ldap_name] = []
if adherent["controle"]:
adherent["controle"] = unicode(adherent["controle"][0]).replace('c','')
if not adherent["controle"][0]:
adherent["controle"] = []
adherent.validate_changes()
adherent.history_gen()
adherent.save()
# On s'en va en mettant à jour dans la continuation la valeur de obj
raise Continue(cont(adherent=adherent))
(code, output) = self.handle_dialog(cont, box)
# On transforme la liste des cases dialog cochée en dictionnnaire
values = dict((a[0], a[0] in output) for a in choices)
# Une continuation que l'on suivra si quelque chose se passe mal
retry_cont = TailCall(self.adherent_carte_etudiant, adherent=adherent, cont=cont, values=values)
return self.handle_dialog_result(
code=code,
output=output,
cancel_cont=cancel_cont if cancel_cont else cont,
error_cont=retry_cont,
codes_todo=[([self.dialog.DIALOG_OK], todo, [values, adherent, cont])]
)
def adherent_charte(self, cont, adherent): def adherent_charte(self, cont, adherent):
a = attributs a = attributs
attribs = [a.charteMA] attribs = [a.charteMA]
@ -547,15 +616,6 @@ class Dialog(proprio.Dialog):
with self.conn.search(dn=adherent.dn, scope=0, mode='rw')[0] as adherent: with self.conn.search(dn=adherent.dn, scope=0, mode='rw')[0] as adherent:
for (key, values) in attrs.items(): for (key, values) in attrs.items():
adherent[key] = values adherent[key] = values
# On retire les éventuelle bl mail invalide
if key == u'mailExt' or key == u'mail':
for bl in adherent['blacklist']:
now = int(time.time())
if bl['type'] == u'mail_invalide' and bl['fin'] > now:
bl['fin'] = now
if bl['debut'] > now:
bl['debut'] = now
bl['comm'] += u'- mail rectifié'
adherent.validate_changes() adherent.validate_changes()
adherent.history_gen() adherent.history_gen()
adherent.save() adherent.save()
@ -593,13 +653,6 @@ class Dialog(proprio.Dialog):
if self.confirm_item(adherent, title="Créer l'adhérent suivant ?"): if self.confirm_item(adherent, title="Créer l'adhérent suivant ?"):
adherent.validate_changes() adherent.validate_changes()
adherent.create() adherent.create()
if make_compte_crans:
if self.dialog.yesno("Imprimer un ticket avec un mot de passe attribué automatiquement ?",
title="Impression de ticket pour %s %s" % (adherent.get('prenom', [''])[0], adherent["nom"][0]),
timeout=self.timeout
) == self.dialog.DIALOG_OK:
subprocess.call(['/usr/scripts/cransticket/dump_creds.py', '--forced', '--pass', 'aid=%s' % adherent['aid'][0]])
self.display_item(adherent, "Impression du ticket en cours ...")
else: else:
adherent = None adherent = None
return adherent return adherent
@ -863,11 +916,13 @@ class Dialog(proprio.Dialog):
"""Crée un adhérent et potentiellement son compte crans avec lui""" """Crée un adhérent et potentiellement son compte crans avec lui"""
def mycont(adherent=None, **kwargs): def mycont(adherent=None, **kwargs):
if adherent: if adherent:
# Une fois l'adhérent créé, on vois s'il adhére/prend la connexion internet # Une fois l'adhérent créé, on vois s'il donne sa carte étudiant et s'il adhére/prend la connexion internet
#adh_cont = TailCall(self.modif_adherent, cont=cont, adherent=adherent) #adh_cont = TailCall(self.modif_adherent, cont=cont, adherent=adherent)
conn_cont = TailCall(self.adherent_connexion, cont=cont(proprio=adherent), adherent=adherent) conn_cont = TailCall(self.adherent_connexion, cont=cont(proprio=adherent), adherent=adherent)
etude_cont = TailCall(self.adherent_etudes, cont=conn_cont, adherent=adherent) carte_cont = TailCall(self.adherent_carte_etudiant, cont=conn_cont, adherent=adherent)
etude_cont = TailCall(self.adherent_etudes, cont=carte_cont, adherent=adherent)
etude_cont(cancel_cont=etude_cont) etude_cont(cancel_cont=etude_cont)
carte_cont(cancel_cont=etude_cont)
# Comme on crée une facture, pas de retour possible # Comme on crée une facture, pas de retour possible
conn_cont(cancel_cont=conn_cont) conn_cont(cancel_cont=conn_cont)
raise Continue(etude_cont) raise Continue(etude_cont)

View file

@ -13,7 +13,7 @@ import traceback
if '/usr/scripts' not in sys.path: if '/usr/scripts' not in sys.path:
sys.path.append('/usr/scripts') sys.path.append('/usr/scripts')
from gestion import affichage from gestion.affich_tools import coul
import gestion.config as config import gestion.config as config
import lc_ldap.objets as objets import lc_ldap.objets as objets
@ -37,14 +37,10 @@ class Dialog(lc.Dialog):
index = 0 index = 0
for bl in obj['blacklist']: for bl in obj['blacklist']:
choices.append( choices.append(
( (str(index),
str(index), coul("%s [%s]" % (bl['type'], bl['comm']), 'rouge' if bl['actif'] else None,
affichage.style( dialog=True)
"%s [%s]" % (bl['type'], bl['comm']), )
'rouge' if bl['actif'] else None,
dialog=True
)
)
) )
index+=1 index+=1
return self.dialog.menu( return self.dialog.menu(
@ -149,7 +145,7 @@ class Dialog(lc.Dialog):
fin_tuple = self.get_timestamp(title=title, text="Choisir la date de fin :", fin_tuple = self.get_timestamp(title=title, text="Choisir la date de fin :",
cont=self_cont(bl=bl, tag=tag, bl_type=bl_type, cont=self_cont(bl=bl, tag=tag, bl_type=bl_type,
debut=None, fin=None, comm=None)) debut=None, fin=None, comm=None))
fin = int(time.mktime(time.struct_time(fin_tuple + (0, 0, -1)))) fin = int(time.mktime(time.struct_time(debut_tuple + (0, 0, -1))))
else: else:
fin = '-' fin = '-'
bl['debut']=debut bl['debut']=debut

View file

@ -6,18 +6,16 @@ Copyright (C) Valentin Samir
Licence : GPLv3 Licence : GPLv3
""" """
import os
import sys import sys
import time import time
import ldap import ldap
import traceback import traceback
import locale
if '/usr/scripts' not in sys.path: if '/usr/scripts' not in sys.path:
sys.path.append('/usr/scripts') sys.path.append('/usr/scripts')
from pythondialog import Dialog from pythondialog import Dialog
from pythondialog import error as DialogError from pythondialog import error as DialogError
from gestion import affichage from gestion.affich_tools import get_screen_size, coul
import lc_ldap.shortcuts import lc_ldap.shortcuts
import lc_ldap.objets as objets import lc_ldap.objets as objets
@ -29,17 +27,12 @@ from CPS import TailCall, tailcaller, Continue, TailCaller
class Dialog(CPS.Dialog): class Dialog(CPS.Dialog):
def __init__(self, debug_enable=False, ldap_test=False, custom_user=None): def __init__(self, debug_enable=False, ldap_test=False, custom_user=None):
super(Dialog, self).__init__() super(Dialog, self).__init__(debug_enable=debug_enable)
# On initialise le moteur de rendu en spécifiant qu'on va faire du dialog # On initialise le moteur de rendu en spécifiant qu'on va faire du dialog
printing.template(dialog=True) printing.template(dialog=True)
self.ldap_test = ldap_test self.ldap_test = ldap_test
if custom_user:
custom_user = custom_user.decode(locale.getdefaultlocale()[1] or "ascii")
self.custom_user = custom_user self.custom_user = custom_user
self.check_ldap() self.check_ldap()
login = self.conn.current_login
dialogrc='/home/%s/.dialogrc' % login
super(Dialog, self).__init__(debug_enable=debug_enable, dialogrc=dialogrc)
def has_right(self, liste, obj=None): def has_right(self, liste, obj=None):
"""Vérifie que l'un des droits de l'utilisateur courant est inclus dans list""" """Vérifie que l'un des droits de l'utilisateur courant est inclus dans list"""
@ -347,7 +340,7 @@ class Dialog(CPS.Dialog):
# pour prendre en compte la largeur du widget dialog # pour prendre en compte la largeur du widget dialog
del items[:] # On vide la liste pour la modifier en place del items[:] # On vide la liste pour la modifier en place
items_id = {} items_id = {}
(col, line) = affichage.getTerminalSize() (line, col) = get_screen_size()
for c in classes: for c in classes:
items.extend(olist[c]) items.extend(olist[c])
items_s = printing.sprint_list(olist[c], col-20).encode('utf-8').split('\n') items_s = printing.sprint_list(olist[c], col-20).encode('utf-8').split('\n')

View file

@ -12,7 +12,6 @@ if '/usr/scripts' not in sys.path:
import lc_ldap.objets as objets import lc_ldap.objets as objets
import lc_ldap.attributs as attributs import lc_ldap.attributs as attributs
import subprocess
import certificat import certificat
import blacklist import blacklist
@ -35,12 +34,10 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
""" """
a = attributs a = attributs
# Quel sont les attributs ldap dont on veut afficher et la taille du champs d'édition correspondant # Quel sont les attributs ldap dont on veut afficher et la taille du champs d'édition correspondant
to_display = [(a.host, 30), (a.macAddress, 17), (a.ipHostNumber, 15)] to_display = [(a.host, 30), (a.macAddress, 17), (a.ipHostNumber, 15),
(a.portTCPout, 50), (a.portTCPin, 50), (a.portUDPout, 50),
to_display_port = [(a.portTCPout, 50), (a.portTCPin, 50), (a.portUDPout, 50), (a.portUDPin, 50)
(a.portUDPin, 50)] ]
to_display_borne = [(a.canal, 10), (a.hotspot, 10), (a.puissance, 10), (a.positionBorne, 50), (a.nvram, 10)]
# Quel séparateur on utilise pour les champs multivalué # Quel séparateur on utilise pour les champs multivalué
separateur = ' ' separateur = ' '
@ -61,19 +58,15 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
title="Paramètres machine", title="Paramètres machine",
backtitle="Gestion des machines du Crans") backtitle="Gestion des machines du Crans")
def check_host(host, objectClass, realm): def check_host(host, objectClass):
# Si c'est une machine wifi, host doit finir par wifi.crans.org # Si c'est une machine wifi, host doit finir par wifi.crans.org
if "machineWifi" == objectClass or 'borneWifi' == objectClass or realm == 'bornes': if "machineWifi" == objectClass or 'borneWifi' == objectClass:
hostend = ".wifi.crans.org" hostend = ".wifi.crans.org"
# Si c'est une machine wifi, host doit finir par crans.org # Si c'est une machine wifi, host doit finir par crans.org
elif "machineFixe" == objectClass or realm == 'serveurs': elif "machineFixe" == objectClass:
hostend = ".crans.org" hostend = ".crans.org"
# Si l'object class est machineCrans, pas de vérification # Si l'object class est machineCrans, pas de vérification
elif "machineCrans" == objectClass: elif "machineCrans" == objectClass:
if realm == 'adm':
hostend = ".adm.crans.org"
if not '.' in host:
host = host + hostend
return host return host
# Sinon, libre à chachun d'ajouter d'autres objectClass ou de filtrer # Sinon, libre à chachun d'ajouter d'autres objectClass ou de filtrer
# plus finement fonction des droits de self.conn.droits # plus finement fonction des droits de self.conn.droits
@ -81,7 +74,7 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
raise ValueError("La machine n'est ni une machine fixe, ni une machine wifi mais %s ?!?" % objectClass) raise ValueError("La machine n'est ni une machine fixe, ni une machine wifi mais %s ?!?" % objectClass)
if not host.endswith(hostend) and not '.' in host: if not host.endswith(hostend) and not '.' in host:
host = host + hostend host = "%s.wifi.crans.org" % host
elif host.endswith(hostend) and '.' in host[:-len(hostend)]: elif host.endswith(hostend) and '.' in host[:-len(hostend)]:
raise ValueError("Nom d'hôte invalide, devrait finir par %s et être sans point dans la première partie" % hostend) raise ValueError("Nom d'hôte invalide, devrait finir par %s et être sans point dans la première partie" % hostend)
elif not host.endswith(hostend) and '.' in host: elif not host.endswith(hostend) and '.' in host:
@ -92,8 +85,7 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
def modif_machine(machine, attrs): def modif_machine(machine, attrs):
with self.conn.search(dn=machine.dn, scope=0, mode='rw')[0] as machine: with self.conn.search(dn=machine.dn, scope=0, mode='rw')[0] as machine:
for (key, values) in attrs.items(): for (key, values) in attrs.items():
if values!=u'<automatique>' or key != 'ipHostNumber': machine[key]=values
machine[key]=values
machine.validate_changes() machine.validate_changes()
machine.history_gen() machine.history_gen()
machine.save() machine.save()
@ -109,18 +101,13 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
} }
with self.conn.newMachine(proprio.dn, realm, ldif) as machine: with self.conn.newMachine(proprio.dn, realm, ldif) as machine:
for (key, values) in attrs.items(): for (key, values) in attrs.items():
if values!=u'<automatique>' or key != u'ipHostNumber': machine[key]=values
machine[key]=values
if attributs.ipsec in machine.attribs: if attributs.ipsec in machine.attribs:
machine[attributs.ipsec.ldap_name]=attributs.ipsec.default machine[attributs.ipsec.ldap_name]=attributs.ipsec.default
machine.validate_changes() machine.validate_changes()
if self.confirm_item(machine, "Voulez vous vraiement créer cette machine ?"): if self.confirm_item(machine, "Voulez vous vraiement créer cette machine ?"):
machine.create() machine.create()
self.display_item(machine, "La machine a bien été créée", ipsec=True) self.display_item(machine, "La machine à bien été créée", ipsec=True)
if realm == 'wifi-adh':
if self.dialog.yesno("Imprimer un ticket pour la machine ?", timeout=self.timeout, title="Impression de ticket", width=50) == self.dialog.DIALOG_OK:
subprocess.call(['/usr/scripts/cransticket/dump_creds.py', '--forced', 'mid=%s' % machine['mid'][0]])
self.display_item(machine, "Impression du ticket ...", ipsec=True)
return machine return machine
else: else:
raise Continue(cont) raise Continue(cont)
@ -136,7 +123,7 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
values = [v for v in values.split(separateur) if v] values = [v for v in values.split(separateur) if v]
# Pour host, on fait quelques vérification de syntaxe # Pour host, on fait quelques vérification de syntaxe
if a.ldap_name == 'host': if a.ldap_name == 'host':
attrs[a.ldap_name]=check_host(values, objectClass, realm) attrs[a.ldap_name]=check_host(values, objectClass)
else: else:
attrs[a.ldap_name]=values attrs[a.ldap_name]=values
# Soit on édite une machine existante # Soit on édite une machine existante
@ -147,16 +134,10 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
machine = create_machine(proprio, realm, attrs) machine = create_machine(proprio, realm, attrs)
raise Continue(cont(machine=machine)) raise Continue(cont(machine=machine))
if machine: if machine:
objectClass = machine["objectClass"][0] objectClass = machine["objectClass"][0]
if self.has_right(a.nounou, proprio):
to_display += to_display_port
# Les bornes wifi ont un to_display différent
if objectClass == 'borneWifi':
to_display += to_display_borne
(code, tags) = self.handle_dialog(cont, box) (code, tags) = self.handle_dialog(cont, box)
# On prépare les fiels à afficher à l'utilisateur si une erreure à lieu # On prépare les fiels à afficher à l'utilisateur si une erreure à lieu
@ -207,8 +188,8 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
menu_droits = { menu_droits = {
'Information' : [a.parent, a.cableur, a.nounou], 'Information' : [a.parent, a.cableur, a.nounou],
'Autre': [a.parent, a.cableur, a.nounou], 'Autre': [a.parent, a.cableur, a.nounou],
'Blackliste':[a.nounou], 'Blackliste':[a.cableur, a.nounou],
'Certificat': [a.parent, a.nounou], 'Certificat': [a.parent, a.cableur, a.nounou],
'Exemption' : [a.nounou], 'Exemption' : [a.nounou],
'Alias' : [a.parent, a.cableur, a.nounou], 'Alias' : [a.parent, a.cableur, a.nounou],
'Remarques' : [a.cableur, a.nounou], 'Remarques' : [a.cableur, a.nounou],
@ -270,41 +251,24 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
menu_droits = { menu_droits = {
'Fixe' : [a.soi, a.cableur, a.nounou], 'Fixe' : [a.soi, a.cableur, a.nounou],
'Wifi' : [a.soi, a.cableur, a.nounou], 'Wifi' : [a.soi, a.cableur, a.nounou],
'Appartements': [a.soi, a.cableur, a.nounou],
} }
menu = { menu = {
'Fixe' : {'text' : "Machine filaire", 'objectClass':'machineFixe', 'realm':'adherents'}, 'Fixe' : {'text' : "Machine filaire", 'objectClass':'machineFixe', 'realm':'adherents'},
'Appartements' : {'text' : "Machine filaire de personnel ENS", 'objectClass':'machineFixe', 'realm':'personnel-ens'},
'Wifi' : {'text': 'Machine sans fil', 'objectClass':'machineWifi', 'realm':'wifi-adh'}, 'Wifi' : {'text': 'Machine sans fil', 'objectClass':'machineWifi', 'realm':'wifi-adh'},
} }
menu_order = ['Wifi'] menu_order = ['Fixe', 'Wifi']
# Machine appartement pour les personnels, fixe pour les autres
if proprio.get('etudes', [False])[0] == u'Personnel ENS':
menu_order.append('Appartements')
else:
# On vérifie que un non MA a qu'une machine fixe
menu_order.append('Fixe')
if not bool(proprio.get('droits', False)) and isinstance(proprio, objets.adherent):
for machine in proprio.machines():
if isinstance(machine, objets.machineFixe):
menu_order.remove('Fixe')
break
if isinstance(proprio, objets.AssociationCrans): if isinstance(proprio, objets.AssociationCrans):
menu_droits.update({ menu_droits.update({
'Fixe' : [a.nounou], 'Fixe' : [a.nounou],
'Wifi' : [a.nounou], 'Wifi' : [a.nounou],
'Wifi-v6' : [a.nounou],
'Adm' : [a.nounou], 'Adm' : [a.nounou],
}) })
menu.update({ menu.update({
'Fixe' : {'text' : "Ajouter un serveur sur le vlan adherent", 'objectClass':'machineCrans', 'realm':'serveurs'}, 'Fixe' : {'text' : "Ajouter un serveur sur le vlan adherent", 'objectClass':'machineCrans', 'realm':'serveurs'},
'Wifi' : {'text': 'Ajouter une borne WiFi sur le vlan wifi', 'objectClass':'borneWifi', 'realm':'bornes'}, 'Wifi' : {'text': 'Ajouter une borne WiFi sur le vlan wifi', 'objectClass':'borneWifi', 'realm':'bornes'},
'Wifi-v6' : {'text': 'Ajouter une borne WiFi sur le vlan wifi en ipv6 only', 'objectClass':'borneWifi', 'realm':'bornes-v6'},
'Adm' : {'text' : "Ajouter un serveur sur le vlan adm", "objectClass":"machineCrans", 'realm':'adm'}, 'Adm' : {'text' : "Ajouter un serveur sur le vlan adm", "objectClass":"machineCrans", 'realm':'adm'},
}) })
menu_order += ['Adm', 'Wifi-v6'] menu_order.append('Adm')
def box(default_item=None): def box(default_item=None):
return self.dialog.menu( return self.dialog.menu(
"Type de Machine ?", "Type de Machine ?",

View file

@ -85,10 +85,11 @@ class Dialog(machine.Dialog, blacklist.Dialog):
@tailcaller @tailcaller
def set_password(proprio, update_obj, cont): def set_password(proprio, update_obj, cont):
if self.dialog.yesno("Attribuer un mot de passe maintenant ? (Vous aurez la possibilité d'imprimer un ticket plus tard également ...)", if self.dialog.yesno("Attribuer un mot de passe maintenant ?",
title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
timeout=self.timeout timeout=self.timeout
) == self.dialog.DIALOG_OK: ) == self.dialog.DIALOG_OK:
#return self.proprio_compte_password(proprio=proprio, return_obj=return_obj, cont=cont(**{update_obj:proprio}))
proprio = self.proprio_compte_password(proprio=proprio, return_obj=True, cont=TailCall(set_password, proprio, update_obj, cont)) proprio = self.proprio_compte_password(proprio=proprio, return_obj=True, cont=TailCall(set_password, proprio, update_obj, cont))
if return_obj: if return_obj:
return proprio return proprio
@ -167,7 +168,7 @@ class Dialog(machine.Dialog, blacklist.Dialog):
raise Continue(cont(proprio=proprio)) raise Continue(cont(proprio=proprio))
#(code, passwords) = self.handle_dialog(cont, box) #(code, passwords) = self.handle_dialog(cont, box)
(code, passwords) = (self.dialog.DIALOG_OK, "") (code, passwords) = (self.dialog.DIALOG_OK, "")
self_cont = TailCall(self.proprio_compte_password, proprio=proprio, cont=cont, return_obj=return_obj) self_cont = TailCall(self.proprio_compte_password, proprio=proprio, cont=cont)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
output=passwords, output=passwords,
@ -410,19 +411,14 @@ class Dialog(machine.Dialog, blacklist.Dialog):
"cheque" : "Chèque", "cheque" : "Chèque",
"carte" : "Par carte bancaire", "carte" : "Par carte bancaire",
"solde" : "Solde Crans (actuel : %s€)", "solde" : "Solde Crans (actuel : %s€)",
"note" : "Note kfet (attention, moins tracable...)",
"arbitraire" : "Création ou destruction magique d'argent.",
} }
def box_choose_paiment(tag, articles): def box_choose_paiment(tag, articles):
box_paiement_order = ["liquide", "cheque", "carte","note"] box_paiement_order = ["liquide", "cheque", "carte"]
if "cransAccount" in proprio['objectClass']: if "cransAccount" in proprio['objectClass']:
if not "SOLDE" in [art['code'] for art in articles] and proprio["solde"]: if not "SOLDE" in [art['code'] for art in articles] and proprio["solde"]:
box_paiement_order.append("solde") box_paiement_order.append("solde")
box_paiement["solde"] = box_paiement["solde"] % proprio["solde"][0] box_paiement["solde"] = box_paiement["solde"] % proprio["solde"][0]
if len(articles) == 1 and "SOLDE" in [art['code'] for art in articles]:
box_paiement_order.append("arbitraire")
choices = [] choices = []
for key in box_paiement_order: for key in box_paiement_order:
choices.append((key, box_paiement[key], 1 if key == tag else 0)) choices.append((key, box_paiement[key], 1 if key == tag else 0))
@ -461,7 +457,6 @@ class Dialog(machine.Dialog, blacklist.Dialog):
def box_choose_item(tags): def box_choose_item(tags):
choices = [] choices = []
gestion.config.factures.ITEMS.update(gestion.config.factures.ITEM_SOLDE)
for code, article in gestion.config.factures.ITEMS.items(): for code, article in gestion.config.factures.ITEMS.items():
choices.append((code, u"%s%s" % (article['designation'], (u' (%s€)' % article['pu']) if article['pu'] != '*' else ""), 1 if code in tags else 0)) choices.append((code, u"%s%s" % (article['designation'], (u' (%s€)' % article['pu']) if article['pu'] != '*' else ""), 1 if code in tags else 0))
return self.dialog.checklist( return self.dialog.checklist(
@ -504,19 +499,11 @@ class Dialog(machine.Dialog, blacklist.Dialog):
def paiement(have_set, tag, proprio, comment, cancel_cont, cont): def paiement(have_set, tag, proprio, comment, cancel_cont, cont):
articles = copy.deepcopy(have_set) articles = copy.deepcopy(have_set)
# On formate les articles
for article in articles: for article in articles:
if article['pu'] == '*': if article['pu'] == '*':
article['pu'] = article['nombre'] article['pu'] = article['nombre']
article['nombre'] = 1 article['nombre'] = 1
# En arbitraire, on accepte que le solde
if tag == u"arbitraire":
if len(articles) > 1 or "SOLDE" not in [art['code'] for art in articles]:
raise ValueError("Il n'est possible que de faire une opération de solde en mode arbitraire")
# Les articles classiques on facture
with self.conn.newFacture(proprio.dn, {}) as facture: with self.conn.newFacture(proprio.dn, {}) as facture:
facture['modePaiement']=unicode(tag, 'utf-8') facture['modePaiement']=unicode(tag, 'utf-8')
facture['article']=articles facture['article']=articles
@ -530,13 +517,13 @@ class Dialog(machine.Dialog, blacklist.Dialog):
arts = ["%s %s" % (art['nombre'], art['designation']) for art in facture['article'] if art['code'] != 'SOLDE'] arts = ["%s %s" % (art['nombre'], art['designation']) for art in facture['article'] if art['code'] != 'SOLDE']
if arts: if arts:
self.dialog.msgbox( self.dialog.msgbox(
text=u"Vous pouvez remettre à l'adherent les articles (si ce sont des articles) suivant :\n * %s" % '\n * '.join(arts), text=u"Vous pouvez remettre à l'adherent les articles (si se sont des articles) suivant :\n * %s" % '\n * '.join(arts),
title=u"Vente terminée", title=u"Vente terminée",
width=0, height=0, timeout=self.timeout) width=0, height=0, timeout=self.timeout)
if tag == "solde": if tag == "solde":
self.dialog.msgbox(text=u"Le solde de l'adhérent a bien été débité", title="Solde débité", width=0, height=0, timeout=self.timeout) self.dialog.msgbox(text=u"Le solde de l'adhérent à bien été débité", title="Solde débité", width=0, height=0, timeout=self.timeout)
if [a for a in facture['article'] if art['code'] == 'SOLDE']: if [a for a in facture['article'] if art['code'] == 'SOLDE']:
self.dialog.msgbox(text=u"Le solde de l'adhérent a bien été crédité", title="Solde crédité", width=0, height=0, timeout=self.timeout) self.dialog.msgbox(text=u"Le solde de l'adhérent à bien été crédité", title="Solde crédité", width=0, height=0, timeout=self.timeout)
else: else:
if not self.confirm(text=u"Le paiement n'a pas été reçue.\n Annuler la vente ?", title="Annulation de la vente", defaultno=True): if not self.confirm(text=u"Le paiement n'a pas été reçue.\n Annuler la vente ?", title="Annulation de la vente", defaultno=True):
raise Continue(cancel_cont) raise Continue(cancel_cont)
@ -572,7 +559,6 @@ class Dialog(machine.Dialog, blacklist.Dialog):
else: else:
(code, tags) = self.handle_dialog(cont, box_choose_item, tags) (code, tags) = self.handle_dialog(cont, box_choose_item, tags)
self_cont=self_cont(tags=tags, have_set=[], to_set=[], tag_paiment=None) self_cont=self_cont(tags=tags, have_set=[], to_set=[], tag_paiment=None)
gestion.config.factures.ITEMS.update(gestion.config.factures.ITEM_SOLDE)
return self.handle_dialog_result( return self.handle_dialog_result(
code=code, code=code,
output=tags, output=tags,

File diff suppressed because it is too large Load diff

View file

@ -176,22 +176,14 @@ class home:
### Redirection ### Redirection
if mail_redirect: if mail_redirect:
write_in_forward = True file(home + '/.forward', 'w').write(mail_redirect + '\n')
# On vérifie s'il y a déjà un .forward
if os.path.exists(os.path.join(home, ".forward")):
write_in_forward = False
if write_in_forward:
with open(os.path.join(home, '.forward'), 'w') as forward_file:
forward_file.write(mail_redirect + '\n')
os.chown(home + '/.forward', int(uid), gid) os.chown(home + '/.forward', int(uid), gid)
os.chmod(home + '/.forward', 0600) os.chmod(home + '/.forward', 0604)
### Owncloud dans le home ### Owncloud dans le home
if not os.path.exists(home + '/OwnCloud'): if not os.path.exists(home + '/OwnCloud'):
os.mkdir(home + '/OwnCloud') os.mkdir(home + '/OwnCloud')
os.chown(home + '/OwnCloud', int(uid), grp.getgrnam('www-data').gr_gid) os.chown(home + '/OwnCloud', int(uid), grp.getgrnam('www-data').gr_gid)
os.chmod(home + '/OwnCloud', 0770) os.chmod(home + '/OwnCloud',0770)
except: except:
print ERREUR print ERREUR
if self.debug: if self.debug:

View file

@ -43,7 +43,6 @@ class autostatus(gen_config) :
"obm.crans.org", "obm.crans.org",
"obm.adm.crans.org", "obm.adm.crans.org",
"batv-3.adm.crans.org", "batv-3.adm.crans.org",
"batv-1.adm.crans.org",
# Config par défaut # Config par défaut
"non-configure.wifi.crans.org", "non-configure.wifi.crans.org",
@ -71,7 +70,6 @@ class autostatus(gen_config) :
"ragnarok.crans.org", # RIP contrôleur disque... "ragnarok.crans.org", # RIP contrôleur disque...
"zamok.crans.org", # c'est en fait fx "zamok.crans.org", # c'est en fait fx
"bati-2.adm.crans.org", # N'est plus en place "bati-2.adm.crans.org", # N'est plus en place
"batv-1.crans.org",
# Bornes wifi de test # Bornes wifi de test
"bullet5.wifi.crans.org", "bullet5.wifi.crans.org",
@ -144,10 +142,8 @@ class autostatus(gen_config) :
infos_routeurs = {} infos_routeurs = {}
infos_routeurs [ '138.231.136.4' ] = ['Odlyd', u'Routeur principal du CRANS'] infos_routeurs [ '138.231.136.4' ] = ['Odlyd', u'Routeur principal du CRANS']
infos_routeurs [ '138.231.136.3' ] = ['Komaz', u'Routeur secondaire du CRANS']
infos_routeurs [ '138.231.132.1' ] = ['Pioneer.zrt', u'Routeur principal de l\'ENS (interne)'] infos_routeurs [ '138.231.132.1' ] = ['Pioneer.zrt', u'Routeur principal de l\'ENS (interne)']
infos_routeurs [ '138.231.132.101' ] = ['Pioneer1.zrt.ens-cachan', u'Routeur principal de l\'ENS (interne)'] infos_routeurs [ '138.231.132.102' ] = ['Pioneer', u'Routeur principal de l\'ENS (interne)']
infos_routeurs [ '138.231.132.102' ] = ['Pioneer2.zrt.ens-cachan', u'Routeur principal de l\'ENS (interne)']
infos_routeurs [ '138.231.176.1' ] = ['Pioneer', u'Routeur principal de l\'ENS'] infos_routeurs [ '138.231.176.1' ] = ['Pioneer', u'Routeur principal de l\'ENS']
infos_routeurs [ '193.49.65.1' ] = ['RenaterCachan1' , u'Routeur Renater' ] infos_routeurs [ '193.49.65.1' ] = ['RenaterCachan1' , u'Routeur Renater' ]
infos_routeurs [ '193.51.181.186' ] = ['RenaterCachan2', u'Routeur Renater'] infos_routeurs [ '193.51.181.186' ] = ['RenaterCachan2', u'Routeur Renater']

View file

@ -73,9 +73,6 @@ class TLSA(ResourceRecord):
if not r_format in ['pem', 'der']: if not r_format in ['pem', 'der']:
raise ValueError("format should be pem or der") raise ValueError("format should be pem or der")
if selector != 0:
raise NotImplementedError("selector different form 0 not implemented")
if cert is None and proto == 'tcp' and name[-1] == '.': if cert is None and proto == 'tcp' and name[-1] == '.':
try: try:
cert = ssl.get_server_certificate((name[:-1], port), ca_certs='/etc/ssl/certs/ca-certificates.crt') cert = ssl.get_server_certificate((name[:-1], port), ca_certs='/etc/ssl/certs/ca-certificates.crt')
@ -93,7 +90,6 @@ class TLSA(ResourceRecord):
raise ValueError("Impossible de convertir le certificat au format DER %s %s %s\n%s" % (name, port, proto, cert)) raise ValueError("Impossible de convertir le certificat au format DER %s %s %s\n%s" % (name, port, proto, cert))
certhex = TLSA.hashCert(reftype, str(dercert)) certhex = TLSA.hashCert(reftype, str(dercert))
self.certhex = certhex
if compat: if compat:
super(TLSA, self).__init__( super(TLSA, self).__init__(
'TYPE52', 'TYPE52',
@ -155,17 +151,13 @@ class TXT(ResourceRecord):
"""Entrée DNS pour un champ TXT""" """Entrée DNS pour un champ TXT"""
def __init__(self, name, value, ttl=None): def __init__(self, name, value, ttl=None):
super(TXT, self).__init__('TXT', name, value, ttl) super(TXT, self).__init__('TXT', name, value, ttl)
if len(self.value) > 200:
self.value = '( "' + '"\n\t\t\t\t"'.join([self.value[x:x+200] for x in xrange(0, len(self.value), 200)]) + '" )'
else:
self.value = '"%s"' % (self.value,)
def __str__(self): def __str__(self):
"""Retourne une chaîne printable dans un fichier bind""" """Retourne une chaîne printable dans un fichier bind"""
if self._ttl: if self._ttl:
return '%s\t%s\tIN\t%s\t%s' % (self.name, self._ttl, self.r_type, self.value) return '%s\t%s\tIN\t%s\t"%s"' % (self.name, self._ttl, self.r_type, self.value)
else: else:
return '%s\tIN\t%s\t%s' % (self.name, self.r_type, self.value) return '%s\tIN\t%s\t"%s"' % (self.name, self.r_type, self.value)
class CNAME(ResourceRecord): class CNAME(ResourceRecord):
"""Entrée DNS pour un alias (toto -> redisdead)""" """Entrée DNS pour un alias (toto -> redisdead)"""
@ -398,14 +390,13 @@ class Zone(ZoneBase):
def add_tlsa_record(self, cert): def add_tlsa_record(self, cert):
"""Ajout d'un certif dans le DNS""" """Ajout d'un certif dans le DNS"""
if 'TLSACert' in cert['objectClass']: if 'TLSACert' in cert['objectClass']:
if not cert.get('revocked', [False])[0]: for host in cert['hostCert']:
for host in cert['hostCert']: nom = self.get_name(host)
nom = self.get_name(host) if nom is None: continue
if nom is None: continue for port in cert['portTCPin']:
for port in cert['portTCPin']: self.add(TLSA(nom, port, 'tcp', cert['certificat'][0], cert['certificatUsage'][0], cert['matchingType'][0], cert['selector'][0], r_format='der'))
self.add(TLSA(nom, port, 'tcp', cert['certificat'][0], cert['certificatUsage'][0], cert['matchingType'][0], cert['selector'][0], r_format='der')) for port in cert['portUDPin']:
for port in cert['portUDPin']: self.add(TLSA(nom, port, 'udp', cert['certificat'][0], cert['certificatUsage'][0], cert['matchingType'][0], cert['selector'][0], r_format='der'))
self.add(TLSA(nom, port, 'udp', cert['certificat'][0], cert['certificatUsage'][0], cert['matchingType'][0], cert['selector'][0], r_format='der'))
def add_machine(self, machine): def add_machine(self, machine):
"""Ajout d'une machine, à savoir chaînage d'ajout """Ajout d'une machine, à savoir chaînage d'ajout
@ -629,12 +620,6 @@ class dns(gen_config):
], ],
} }
DKIM = {
'crans.org': [
TXT('mail._domainkey', 'v=DKIM1; k=rsa; p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtwkNVd9Mmz8S4WcfuPk0X2drG39gS8+uxAv8igRILgzWeN8j2hjeZesl8pm/1UTVU87bYcdfUgXiGfQy9nR5p/Vmt2kS7sXk9nsJ/VYENgb3IJQ6paWupSTFMyeKycJ4ZHCEZB/bVvifoG6vLKqW5jpsfCiOcfdcgXATn0UPuVx9t93yRrhoEMntMv9TSodjqd3FKCtJUoh5cNQHo0T6dWKtxoIgNi/mvZ92D/IACwu/XOU+Rq9fnoEI8GukBQUR5AkP0B/JrvwWXWX/3EjY8X37ljEX0XUdq/ShzTl5iK+CM83stgkFUQh/rpww5mnxYEW3X4uirJ7VJHmY4KPoIU+2DPjLQj9Hz63CMWY3Ks2pXWzxD3V+GI1aJTMFOv2LeHnI3ScqFaKj9FR4ZKMb0OW2BEFBIY3J3aeo/paRwdbVCMM7twDtZY9uInR/NhVa1v9hlOxwp4/2pGSKQYoN2CkAZ1Alzwf8M3EONLKeiC43JLYwKH1uBB1oikSVhMnLjG0219XvfG/tphyoOqJR/bCc2rdv5pLwKUl4wVuygfpvOw12bcvnTfYuk/BXzVHg9t4H8k/DJR6GAoeNAapXIS8AfAScF8QdKfplhKLJyQGJ6lQ75YD9IwRAN0oV+8NTjl46lI/C+b7mpfXCew+p6YPwfNvV2shiR0Ez8ZGUQIcCAwEAAQ==')
],
}
NON_CLONABLE_SPFs = { NON_CLONABLE_SPFs = {
'crans.org': [ 'crans.org': [
TXT(short_name(_mx), 'v=spf1 mx:crans.org ~all') for _mx in config.dns.MXs TXT(short_name(_mx), 'v=spf1 mx:crans.org ~all') for _mx in config.dns.MXs
@ -688,7 +673,7 @@ class dns(gen_config):
# On met les mêmes MX pour toutes les zones. # On met les mêmes MX pour toutes les zones.
zone.extend(self.MXs) zone.extend(self.MXs)
# Les RR définis ici sont ajoutés aux zones idoines, de façon à se simplifier la vie. # Les RR définis ici sont ajoutés aux zones idoines, de façon à se simplifier la vie.
for rr_type in [self.SRVs, self.NAPTRs, self.DSs, self.EXTRAS, self.SPFs, self.NON_CLONABLE_SPFs, self.DKIM]: for rr_type in [self.SRVs, self.NAPTRs, self.DSs, self.EXTRAS, self.SPFs, self.NON_CLONABLE_SPFs]:
if zone.zone_name in rr_type.keys(): if zone.zone_name in rr_type.keys():
zone.extend(rr_type[zone.zone_name]) zone.extend(rr_type[zone.zone_name])
for m in machines: for m in machines:

View file

@ -45,13 +45,9 @@ class exemptions(gen_config):
for machine in machines: for machine in machines:
for destination in machine["exempt"]: for destination in machine["exempt"]:
if destination.value.version == 4: if destination.value.version == 4:
if not machine['ipHostNumber']:
continue
source = str(machine["ipHostNumber"][0]) source = str(machine["ipHostNumber"][0])
requete = "INSERT INTO exemptes (ip_crans, ip_dest) VALUES ('%s','%s')" % (source, destination) requete = "INSERT INTO exemptes (ip_crans, ip_dest) VALUES ('%s','%s')" % (source, destination)
else: else:
if not machine['macAddress']:
continue
source = str(machine["macAddress"][0]) source = str(machine["macAddress"][0])
requete = "INSERT INTO exemptes6 (mac_crans, ip_dest) VALUES ('%s','%s')" % (source, destination) requete = "INSERT INTO exemptes6 (mac_crans, ip_dest) VALUES ('%s','%s')" % (source, destination)
# Si ip vide, passons au suivant # Si ip vide, passons au suivant
@ -90,8 +86,7 @@ class machines(gen_config):
if not m['macAddress'][0].value == '<automatique>': if not m['macAddress'][0].value == '<automatique>':
curseur.execute("INSERT INTO machines (mac_addr, type, id) VALUES ('%s','adherent',%s);" % (m['macAddress'][0], m.proprio()['aid'][0].value)) curseur.execute("INSERT INTO machines (mac_addr, type, id) VALUES ('%s','adherent',%s);" % (m['macAddress'][0], m.proprio()['aid'][0].value))
elif m.proprio().__class__ == lc_ldap.objets.AssociationCrans: elif m.proprio().__class__ == lc_ldap.objets.AssociationCrans:
if not m['macAddress'][0].value == '<automatique>': curseur.execute("INSERT INTO machines (mac_addr, type, id) VALUES ('%s','crans',%s);" % (m['macAddress'][0], m['mid'][0].value))
curseur.execute("INSERT INTO machines (mac_addr, type, id) VALUES ('%s','crans',%s);" % (m['macAddress'][0], m['mid'][0].value))
# on commit # on commit
pgsql.commit() pgsql.commit()

View file

@ -34,13 +34,13 @@ class firewall(utils.firewall_tools) :
self.use_ipset = [self.blacklist_hard, self.test_mac_ip, self.blacklists] self.use_ipset = [self.blacklist_hard, self.test_mac_ip, self.blacklists]
self.ipset['mac_ip']={ self.ipset['mac_ip']={
'adh' : Ipset("MAC-IP-ADH", "bitmap:ip,mac", "range 138.231.136.0-138.231.151.255"), 'adh' : Ipset("MAC-IP-ADH","macipmap","--from 138.231.136.0 --to 138.231.151.255"),
'adm' : Ipset("MAC-IP-ADM", "bitmap:ip,mac", "range 10.231.136.0-10.231.136.255"), 'adm' : Ipset("MAC-IP-ADM","macipmap","--from 10.231.136.0 --to 10.231.136.255"),
'app' : Ipset("MAC-IP-APP", "bitmap:ip,mac", "range 10.2.9.0-10.2.9.255"), 'app' : Ipset("MAC-IP-APP","macipmap","--from 10.2.9.0 --to 10.2.9.255"),
} }
self.ipset['blacklist']={ self.ipset['blacklist']={
'hard' : Ipset("BLACKLIST-HARD", "hash:ip"), 'hard' : Ipset("BLACKLIST-HARD","ipmap","--from 138.231.136.0 --to 138.231.151.255"),
} }
@ -110,7 +110,7 @@ class firewall(utils.firewall_tools) :
if fill_ipset: if fill_ipset:
# On récupère la liste de toutes les ips blacklistés hard # On récupère la liste de toutes les ips blacklistés hard
bl_hard_ips = self.blacklisted_ips(config.blacklist_sanctions) bl_hard_ips = self.blacklisted_ips(config.blacklist_sanctions, config.NETs['all'])
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['hard']) anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['hard'])
self.ipset['blacklist']['hard'].restore(bl_hard_ips) self.ipset['blacklist']['hard'].restore(bl_hard_ips)
print OK print OK
@ -131,7 +131,7 @@ class firewall(utils.firewall_tools) :
def mac_ip_remove(self, mac, ip): def mac_ip_remove(self, mac, ip):
machine = {'macAddress':[mac], 'ipHostNumber': [ip]} machine = {'macAddress':[mac], 'ipHostNumber': [ip]}
self.test_mac_ip_dispatch(lambda set, data: self.ipset['mac_ip'][set].delete(data), machine) self.test_mac_ip_dispatch(lambda set, data: set.ipset['mac_ip'][set].delete(data), machine)
def test_mac_ip_dispatch(self, func, machine): def test_mac_ip_dispatch(self, func, machine):
"""Détermine à quel set de mac-ip appliquer la fonction ``func`` (add, delete, append, ...)""" """Détermine à quel set de mac-ip appliquer la fonction ``func`` (add, delete, append, ...)"""

View file

@ -21,9 +21,8 @@ firewall = {
'odlyd' : komaz.firewall, 'odlyd' : komaz.firewall,
'zamok' : zamok.firewall, 'zamok' : zamok.firewall,
'routeur' : routeur.firewall, 'routeur' : routeur.firewall,
'gordon' : base.firewall_routeur,
'eap' : base.firewall_wifionly, 'eap' : base.firewall_wifionly,
'pea' : base.firewall_wifionly,
'radius' : base.firewall_wifionly
} }
if hostname in firewall.keys(): if hostname in firewall.keys():

View file

@ -33,21 +33,18 @@ class firewall(base.firewall_routeur):
self.use_tc.extend([self.limitation_debit]) self.use_tc.extend([self.limitation_debit])
self.ipset['reseaux_non_routable'] = { self.ipset['reseaux_non_routable'] = {
'deny' : base.Ipset("RESEAUX-NON-ROUTABLE-DENY", "hash:net"), 'deny' : base.Ipset("RESEAUX-NON-ROUTABLE-DENY","nethash"),
'allow' : base.Ipset("RESEAUX-NON-ROUTABLE-ALLOW", "hash:net"), 'allow' : base.Ipset("RESEAUX-NON-ROUTABLE-ALLOW","nethash"),
} }
self.ipset['blacklist'].update({ self.ipset['blacklist'].update({
'soft' : base.Ipset("BLACKLIST-SOFT", "hash:ip"), 'soft' : base.Ipset("BLACKLIST-SOFT","ipmap","--from 138.231.136.0 --to 138.231.151.255"),
'upload' : base.Ipset("BLACKLIST-UPLOAD", "hash:ip"), 'upload' : base.Ipset("BLACKLIST-UPLOAD","ipmap","--from 138.231.136.0 --to 138.231.151.255"),
}) })
# Portail captif/blacklist soft: ipset des gens ayant cliqué pour continuer à naviguer # Portail captif/blacklist soft: ipset des gens ayant cliqué pour continuer à naviguer
self.ipset['confirmation'] = base.Ipset("CONFIRMATION", "hash:ip", "") self.ipset['confirmation'] = base.Ipset("CONFIRMATION", "hash:ip", "")
# Ouvertures de ports temporaires
self.ipset['ip_port_tmp'] = base.Ipset("IP-PORT-TMP", "hash:ip,port", "timeout 3600")
def blacklist_maj(self, ips): def blacklist_maj(self, ips):
"""Mise à jour des blacklistes""" """Mise à jour des blacklistes"""
self.blacklist_hard_maj(ips) self.blacklist_hard_maj(ips)
@ -132,7 +129,6 @@ class firewall(base.firewall_routeur):
self.add(table, chain, '-j %s' % self.ssh_on_https(table)) self.add(table, chain, '-j %s' % self.ssh_on_https(table))
self.add(table, chain, '-j %s' % self.connexion_secours(table)) self.add(table, chain, '-j %s' % self.connexion_secours(table))
self.add(table, chain, '-j %s' % self.blacklist_soft(table)) self.add(table, chain, '-j %s' % self.blacklist_soft(table))
self.add(table, chain, '-j %s' % self.blacklist_hard(table))
chain = 'POSTROUTING' chain = 'POSTROUTING'
self.add(table, chain, '-j %s' % self.connexion_wififederez(table)) self.add(table, chain, '-j %s' % self.connexion_wififederez(table))
@ -251,7 +247,6 @@ class firewall(base.firewall_routeur):
if table == 'nat': if table == 'nat':
pretty_print(table, chain) pretty_print(table, chain)
self.add(table, chain, '-p tcp -d 138.231.136.2 --dport 22 -j DNAT --to-destination 138.231.136.1:22') # redirection du ssh vers zamok self.add(table, chain, '-p tcp -d 138.231.136.2 --dport 22 -j DNAT --to-destination 138.231.136.1:22') # redirection du ssh vers zamok
self.add(table, chain, '-p tcp -d 138.231.136.2 --dport 80 -j DNAT --to-destination 138.231.136.1:81') # redirection du ssh vers zamok a travers httptunnel
self.add(table, chain, '-p tcp -d 138.231.136.2 --dport 443 -j DNAT --to-destination 138.231.136.1:22') # redirection du ssh vers zamok (pour passer dans un proxy, avec corkscrew) self.add(table, chain, '-p tcp -d 138.231.136.2 --dport 443 -j DNAT --to-destination 138.231.136.1:22') # redirection du ssh vers zamok (pour passer dans un proxy, avec corkscrew)
print OK print OK
@ -347,7 +342,7 @@ class firewall(base.firewall_routeur):
if fill_ipset: if fill_ipset:
# On récupère la liste de toutes les ips blacklistés soft # On récupère la liste de toutes les ips blacklistés soft
bl_soft_ips = self.blacklisted_ips(base.config.blacklist_sanctions_soft) bl_soft_ips = self.blacklisted_ips(base.config.blacklist_sanctions_soft, base.config.NETs['all'])
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['soft']) anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['soft'])
self.ipset['blacklist']['soft'].restore(bl_soft_ips) self.ipset['blacklist']['soft'].restore(bl_soft_ips)
print OK print OK
@ -372,41 +367,6 @@ class firewall(base.firewall_routeur):
self.apply(table, chain) self.apply(table, chain)
return chain return chain
def blacklist_hard(self, table=None, fill_ipset=False, apply=False):
"""Bloque tout, sauf le 80 pour afficher le portail captif"""
chain = 'BLACKLIST_HARD'
if fill_ipset:
# On récupère la liste de toutes les ips blacklistés hard
bl_hard_ips = self.blacklisted_ips(base.config.blacklist_sanctions)
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['hard'])
self.ipset['blacklist']['hard'].restore(bl_hard_ips)
print OK
if table == 'filter':
pretty_print(table, chain)
# Same as blacklist_soft: autorise le port 80 et 3128 vers soi-même
self.add(table, chain, '-p tcp --dport 80 -m set --match-set %s src -j ACCEPT' % self.ipset['blacklist']['hard'] )
self.add(table, chain, '-p tcp --sport 80 -m set --match-set %s dst -j ACCEPT' % self.ipset['blacklist']['hard'] )
self.add(table, chain, '-p tcp -d 10.231.136.4 --dport 3128 -m set --match-set %s src -j ACCEPT' % self.ipset['blacklist']['hard'] )
self.add(table, chain, '-p tcp -s 10.231.136.4 --sport 3128 -m set --match-set %s dst -j ACCEPT' % self.ipset['blacklist']['hard'] )
# Mais on continue en refusant le reste
self.add(table, chain, '-m set --match-set %s src -j REJECT' % self.ipset['blacklist']['hard'] )
self.add(table, chain, '-m set --match-set %s dst -j REJECT' % self.ipset['blacklist']['hard'] )
print OK
if table == 'nat':
pretty_print(table, chain)
for net in base.config.NETs['all']:
self.add(table, chain, '-d %s -j RETURN' % net)
self.add(table, chain, '-p tcp --dport 80 -m set --match-set %s src -j RETURN' % self.ipset['confirmation'] ) # Les gens qui ont cliqué -> fine !
self.add(table, chain, '-p tcp --dport 80 -m set --match-set %s src -j DNAT --to-destination 10.231.136.4:3128' % self.ipset['blacklist']['hard'] )
print OK
if apply:
self.apply(table, chain)
return chain
def blacklist_upload_maj(self, ip_list): def blacklist_upload_maj(self, ip_list):
self.blacklist_upload(fill_ipset=True) self.blacklist_upload(fill_ipset=True)
# for ip in ip_list: # for ip in ip_list:
@ -425,7 +385,7 @@ class firewall(base.firewall_routeur):
if fill_ipset: if fill_ipset:
# On récupère la liste de toutes les ips blacklistés pour upload # On récupère la liste de toutes les ips blacklistés pour upload
bl_upload_ips = self.blacklisted_ips(base.config.blacklist_bridage_upload) bl_upload_ips = self.blacklisted_ips(base.config.blacklist_bridage_upload, base.config.NETs['all'])
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['upload']) anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['upload'])
self.ipset['blacklist']['upload'].restore(bl_upload_ips) self.ipset['blacklist']['upload'].restore(bl_upload_ips)
print OK print OK
@ -493,7 +453,6 @@ class firewall(base.firewall_routeur):
if table == 'filter': if table == 'filter':
pretty_print(table, chain) pretty_print(table, chain)
self.add(table, chain, '-m set --match-set %s dst,dst -j ACCEPT' % self.ipset['ip_port_tmp'] )
for net in base.config.NETs['serveurs']: for net in base.config.NETs['serveurs']:
for proto in base.config.firewall.srv_ports_default.keys(): for proto in base.config.firewall.srv_ports_default.keys():
if base.config.firewall.srv_ports_default[proto]['output']: if base.config.firewall.srv_ports_default[proto]['output']:
@ -584,17 +543,17 @@ class firewall(base.firewall_routeur):
utils.tc("class add dev %s parent 1: classid 1:1 " utils.tc("class add dev %s parent 1: classid 1:1 "
"htb rate %s ceil %s" % (dev[int_key], uplink_speed, uplink_speed)) "htb rate %s ceil %s" % (dev[int_key], uplink_speed, uplink_speed))
utils.tc("class add dev %s parent 1:1 classid 1:2 " utils.tc("class add dev %s parent 1:1 classid 1:2 "
"htb rate %smbit ceil %smbit" % (dev[int_key], debit_max[int_key], debit_max[int_key])) "htb rate %smbit ceil %smbit" % (dev[int_key], debit_max, debit_max))
# Classe par defaut # Classe par defaut
utils.tc('class add dev %s parent 1:2 classid 1:10 ' utils.tc('class add dev %s parent 1:2 classid 1:10 '
'htb rate %smbit ceil %smbit prio 1' % (dev[int_key], debit_max[int_key], debit_max[int_key])) 'htb rate %smbit ceil %smbit prio 1' % (dev[int_key], debit_max, debit_max))
utils.tc('qdisc add dev %s parent 1:10 ' utils.tc('qdisc add dev %s parent 1:10 '
'handle 10: sfq perturb 10' % dev[int_key]) 'handle 10: sfq perturb 10' % dev[int_key])
# Classe par pour la voip # Classe par pour la voip
utils.tc('class add dev %s parent 1:2 classid 1:12 ' utils.tc('class add dev %s parent 1:2 classid 1:12 '
'htb rate %smbit ceil %smbit prio 0' % (dev[int_key], debit_max[int_key], debit_max[int_key])) 'htb rate %smbit ceil %smbit prio 0' % (dev[int_key], debit_max, debit_max))
utils.tc('qdisc add dev %s parent 1:12 ' utils.tc('qdisc add dev %s parent 1:12 '
'handle 12: sfq perturb 10' % dev[int_key]) 'handle 12: sfq perturb 10' % dev[int_key])
@ -622,7 +581,7 @@ class firewall(base.firewall_routeur):
# Classe pour le download des apparetments # Classe pour le download des apparetments
utils.tc("class add dev %s parent 1: classid 1:3 " utils.tc("class add dev %s parent 1: classid 1:3 "
"htb rate %smbit ceil %smbit" % (dev[int_key], debit_max['total']/10, debit_max['total']/2)) "htb rate %smbit ceil %smbit" % (dev[int_key], debit_max/10, debit_max/2))
utils.tc('qdisc add dev %s parent 1:3 ' utils.tc('qdisc add dev %s parent 1:3 '
'handle 3: sfq perturb 10' % dev[int_key]) 'handle 3: sfq perturb 10' % dev[int_key])
@ -646,7 +605,7 @@ class firewall(base.firewall_routeur):
# Classe pour le download wifi federez # Classe pour le download wifi federez
utils.tc("class add dev %s parent 1: classid 1:5 " utils.tc("class add dev %s parent 1: classid 1:5 "
"htb rate %smbit ceil %smbit" % (dev[int_key], debit_max['total']/10, debit_max['total']/2)) "htb rate %smbit ceil %smbit" % (dev[int_key], debit_max/10, debit_max/2))
utils.tc('qdisc add dev %s parent 1:5 ' utils.tc('qdisc add dev %s parent 1:5 '
'handle 3: sfq perturb 10' % dev[int_key]) 'handle 3: sfq perturb 10' % dev[int_key])

View file

@ -1,10 +1,11 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import sys import sys
import netaddr import netaddr
if '/usr/scripts' not in sys.path: if '/usr/scripts/' not in sys.path:
sys.path.append('/usr/scripts') sys.path.append('/usr/scripts/')
import syslog import syslog
import subprocess import subprocess
@ -55,7 +56,7 @@ class firewall_tools(object) :
"""Classe de base du pare-feu implémentant l'association mac-ip (pour les machines filaires) et les blacklists hard""" """Classe de base du pare-feu implémentant l'association mac-ip (pour les machines filaires) et les blacklists hard"""
def machines(self): def machines(self):
"""Renvoit la liste de toutes les machines""" """Renvois la liste de toutes les machines"""
if self._machines: if self._machines:
return self._machines return self._machines
# On utilise allMachinesAdherents car on a besoin que # On utilise allMachinesAdherents car on a besoin que
@ -63,37 +64,48 @@ class firewall_tools(object) :
# les blacklistes d'un proprio lorsque l'on regarde les blacklistes # les blacklistes d'un proprio lorsque l'on regarde les blacklistes
# d'une machine # d'une machine
anim('\tChargement des machines') anim('\tChargement des machines')
# On prend toutes les machines y compris celles de ceux qui n'ont pas payé self._machines, self._adherents = self.conn.allMachinesAdherents()
# elles seront ajoutées dans mac_ip mais blacklistées du fait du non paiement ensuite self._adherents = [ adh for adh in self._adherents if adh.paiement_ok() ]
self._machines = self.conn.allMachines()
print OK print OK
return self._machines return self._machines
def adherents(self):
"""
Renvois la liste de tous les adhérents à jour de paiement
(car on suppose que la blackliste paiement est hard)
"""
if self._adherents:
return self._adherents
self._machines, self._adherents = self.conn.allMachinesAdherents()
self._adherents = [ adh for adh in self._adherents if adh.paiement_ok() ]
return self._adherents
def blacklisted_machines(self): def blacklisted_machines(self):
"""Renvoit la liste de toutes les machines ayant une blackliste actives""" """Renvois la liste de toutes les machines ayant une blackliste actives"""
if self._blacklisted_machines: if self._blacklisted_machines:
return self._blacklisted_machines return self._blacklisted_machines
self._blacklisted_machines = [ machine for machine in self.machines() if machine.blacklist_actif() ] self._blacklisted_machines = [ machine for machine in self.machines() if machine.blacklist_actif() ]
return self._blacklisted_machines return self._blacklisted_machines
def blacklisted_ips(self, blacklist_sanctions=None): def blacklisted_ips(self, blacklist_sanctions=None, nets=None):
"""Renvoit l'ensemble des ips des machines ayant une blacklist dans blacklist_sanctions et étant dans nets si spécifié""" """Renvois l'ensemble des ips des machines ayant une blacklist dans blacklist_sanctions et étant dans nets si spécifié"""
bl_ips = set() bl_ips = set()
for machine in self.blacklisted_machines(): for machine in self.blacklisted_machines():
if blacklist_sanctions is None or set(bl['type'] for bl in machine.blacklist_actif()).intersection(blacklist_sanctions): if blacklist_sanctions is None or set(bl['type'] for bl in machine.blacklist_actif()).intersection(blacklist_sanctions):
for ip in machine['ipHostNumber']: for ip in machine['ipHostNumber']:
bl_ips.add(str(ip)) if nets is None:
bl_ips.add(str(ip))
else:
for net in nets:
if ip.value in netaddr.IPNetwork(net):
bl_ips.add(str(ip))
return bl_ips return bl_ips
def blacklisted_adherents(self, excepts=[]): def blacklisted_adherents(self, excepts=[]):
"""Renvoit la liste de tous les adhérents ayant une blackliste active en ignorant les blacklist de excepts""" """Renvois la liste de tous les adhérents ayant une blackliste active en ignorant les blacklist de excepts"""
if not self._adherents:
self._adherents = self.conn.allAdherents()
if self._blacklisted_adherents and self._blacklisted_adherents_type == set(excepts): if self._blacklisted_adherents and self._blacklisted_adherents_type == set(excepts):
return self._blacklisted_adherents return self._blacklisted_adherents
self._blacklisted_adherents = filter(lambda adh: adh.blacklist_actif(excepts), self._adherents) self._blacklisted_adherents = filter(lambda adh: adh.blacklist_actif(excepts), self.adherents())
self._blacklisted_adherents_type = set(excepts) self._blacklisted_adherents_type = set(excepts)
return self._blacklisted_adherents return self._blacklisted_adherents

View file

@ -102,7 +102,7 @@ class firewall(base.firewall):
self.add(table, chain, '-d 127.0.0.1/8 -j RETURN') self.add(table, chain, '-d 127.0.0.1/8 -j RETURN')
for net in base.config.NETs['all']: for net in base.config.NETs['all']:
self.add(table, chain, '-d %s -j RETURN' % net) self.add(table, chain, '-d %s -j RETURN' % net)
for adh in self.blacklisted_adherents(excepts=['paiement']): for adh in self.blacklisted_adherents():
if 'uidNumber' in adh: if 'uidNumber' in adh:
self.add(table, chain, '-m owner --uid-owner %s -j REJECT' % adh['uidNumber'][0]) self.add(table, chain, '-m owner --uid-owner %s -j REJECT' % adh['uidNumber'][0])
print OK print OK

View file

@ -136,7 +136,7 @@ def main_router():
ip6tables.mangle.prerouting('-i %s -m state --state NEW -j LOG --log-prefix "LOG_ALL "' % dev_ip6 ) ip6tables.mangle.prerouting('-i %s -m state --state NEW -j LOG --log-prefix "LOG_ALL "' % dev_ip6 )
# On force le /32 de google à passer en ipv4 pour tester si ça soulage le tunnel ipv6 # On force le /32 de google à passer en ipv4 pour tester si ça soulage le tunnel ipv6
ip6tables.filter.forward('-o %s -p tcp -d 2a00:1450:4006::/32 -j REJECT --reject-with icmp6-addr-unreachable' % dev_ip6) ip6tables.filter.forward('-o %s -p tcp -d 2a00:1450:4006::/32 -j REJECT' % dev_ip6)
# Ipv6 sur évènementiel, on ne laisse sortir que si ça vient de la mac d'ytrap-llatsni # Ipv6 sur évènementiel, on ne laisse sortir que si ça vient de la mac d'ytrap-llatsni
ip6tables.filter.forward('-o %s -d 2a01:240:fe3d:d2::/64 -j ACCEPT' % dev_crans) ip6tables.filter.forward('-o %s -d 2a01:240:fe3d:d2::/64 -j ACCEPT' % dev_crans)

View file

@ -64,7 +64,10 @@ class base_reconfigure:
'warez':__service_develop['blacklist_warez'], 'warez':__service_develop['blacklist_warez'],
'ipv6_ra':__service_develop['blacklist_ipv6_ra'], 'ipv6_ra':__service_develop['blacklist_ipv6_ra'],
'upload': __service_develop['blacklist_upload'], 'upload': __service_develop['blacklist_upload'],
'p2p': __service_develop['blacklist_p2p'],
'autodisc_virus':__service_develop['blacklist_autodisc_virus'],
'autodisc_upload': __service_develop['blacklist_autodisc_upload'], 'autodisc_upload': __service_develop['blacklist_autodisc_upload'],
'autodisc_p2p': __service_develop['blacklist_autodisc_p2p'],
'bloq': __service_develop['blacklist_bloq'], 'bloq': __service_develop['blacklist_bloq'],
}) })
except ImportError: except ImportError:

View file

@ -16,37 +16,31 @@
import sys import sys
if '/usr/scripts' not in sys.path: sys.path.append('/usr/scripts/gestion')
sys.path.append('/usr/scripts')
import commands import commands
import os import os
IPSET_PATH = '/sbin/ipset'
# Avant jessie: ipset était dans /usr/sbin
if not os.path.exists(IPSET_PATH):
IPSET_PATH = '/usr' + IPSET_PATH
class IpsetError(Exception): class IpsetError(Exception):
# Gestion des erreurs d'ipset # Gestion des erreurs d'ipset
def __init__(self, cmd, err_code, output): def __init__(self,cmd,err_code,output):
self.cmd = cmd self.cmd=cmd
self.err_code = err_code self.err_code=err_code
self.output = output self.output=output
def __str__(self): def __str__(self):
return "%s\n status : %s\n %s" % (self.cmd, self.err_code, self.output) return "%s\n status : %s\n %s" % (self.cmd,self.err_code,self.output)
class Ipset(object): class Ipset(object):
ipset = IPSET_PATH ipset="/usr/sbin/ipset"
def __str__(self): def __str__(self):
return self.set return self.set
def __init__(self, set, type, typeopt=''): def __init__(self,set,type,typeopt=''):
self.set = set self.set=set
self.type = type self.type=type
self.typeopt = typeopt self.typeopt=typeopt
self.squeeze = os.uname()[2] < '3'
try: try:
self.create() self.create()
except IpsetError as error: except IpsetError as error:
@ -56,58 +50,62 @@ class Ipset(object):
raise raise
pass pass
def call(self, cmd, arg=''): def call(self,cmd,arg=''):
"""Appel système à ipset""" """Appel système à ipset"""
cmd_line = "%s %s %s %s" % (self.ipset, cmd, self.set, arg) cmd_line="%s %s %s %s" % (self.ipset,cmd,self.set,arg)
status, output = commands.getstatusoutput(cmd_line) status,output=commands.getstatusoutput(cmd_line)
if status: if status:
raise IpsetError(cmd_line, status, output) raise IpsetError(cmd_line,status,output)
return output return output
def create(self, opt=''): def create(self,opt=''):
self.call("create", "%s %s" % (self.type, self.typeopt)) self.call("-N","%s %s" % (self.type, self.typeopt))
def add(self, arg): def add(self,arg):
self.call("add", arg) self.call("-A",arg)
def list(self): def list(self):
output = self.call("list").splitlines() output=self.call("-L").splitlines()
list = [] list=[]
for line in output[6:]: for line in output[6:]:
if line == 'Bindings:': if line=='Bindings:':
break break
list.append(line) list.append(line)
return list return list
def delete(self, ip): def delete(self,ip):
"""Delete an IP""" """Delete an IP"""
self.call("del", ip) self.call("-D",ip)
def restore(self, rules): def restore(self,rules):
""" restore le set courrant""" """ restore le set courrant"""
rules_str = self.restore_format(rules) rules_str=self.restore_format(rules)
str = "%s\nCOMMIT\n" % rules_str if self.squeeze:
path = '/tmp/ipset_%s' % self.set create_str="-N %s %s %s" % (self.set,self.type,self.typeopt)
f = open(path, 'w+') str="%s\n%s\nCOMMIT\n" % (create_str,rules_str)
else:
str="%s\nCOMMIT\n" % rules_str
path='/tmp/ipset_%s' % self.set
f=open(path, 'w+')
f.write(str) f.write(str)
f.close() f.close()
try: try:
self.flush() self.flush()
except IpsetError as error: if self.squeeze:
sys.stderr.write("%s\n" % error) self.destroy()
except IpsetError as error: sys.stderr.write("%s\n" % error)
cmd = "cat %s | %s -R" % (path, self.ipset) cmd="cat %s | %s -R" % (path,self.ipset)
status, output = commands.getstatusoutput(cmd) status,output=commands.getstatusoutput(cmd)
if status: if status:
raise IpsetError(cmd, status, output) raise IpsetError(cmd,status,output)
return output return output
def flush(self): def flush(self):
self.call("flush") self.call("-F")
def destroy(self): def destroy(self):
self.call("destroy") self.call("-X")
def restore_format(self, rules): def restore_format(self,rules):
return '\n'.join(["add %s %s" % (self.set, data) for data in rules]) return '\n'.join(["-A %s %s" % (self.set,data) for data in rules])

View file

@ -28,7 +28,7 @@ console inactivity-timer 30
logging {{ s }} logging {{ s }}
{%- endfor %} {%- endfor %}
;--- IP du switch --- ;--- IP du switch ---
ip default-gateway {{ gateway }} ip default-gateway 10.231.136.4
{%- for vlan in vlans %} {%- for vlan in vlans %}
vlan {{ vlan.id }} vlan {{ vlan.id }}
name "{{ vlan.name|capitalize }}" name "{{ vlan.name|capitalize }}"
@ -54,13 +54,12 @@ no web-management
aaa authentication ssh login public-key none aaa authentication ssh login public-key none
aaa authentication ssh enable public-key none aaa authentication ssh enable public-key none
ip ssh ip ssh
ip authorized-managers {{ network_id }} {{ subnet }} ip authorized-managers 10.231.136.0 255.255.255.0
ip ssh filetransfer ip ssh filetransfer
;--- Protection contre les boucles --- ;--- Protection contre les boucles ---
loop-protect disable-timer 30 loop-protect disable-timer 30
loop-protect transmit-interval 3 loop-protect transmit-interval 3
loop-protect {{ non_trusted }} loop-protect {{ non_trusted }}
{%- if not public %}
;--- Serveurs radius --- ;--- Serveurs radius ---
radius-server dead-time 2 radius-server dead-time 2
radius-server key {{ radius_key }} radius-server key {{ radius_key }}
@ -69,7 +68,6 @@ radius-server host {{ s }}
{%- endfor %} {%- endfor %}
;--- Filtrage mac --- ;--- Filtrage mac ---
aaa port-access mac-based addr-format multi-colon aaa port-access mac-based addr-format multi-colon
{%- endif %}
;--- Bricoles --- ;--- Bricoles ---
no cdp run no cdp run
no stack no stack
@ -88,7 +86,7 @@ no ipv6 ra-guard ports {{ trusted }}
{% endif %} {% endif %}
;--- Config des prises --- ;--- Config des prises ---
{%- for port in ports %} {%- for port in ports %}
{%- if port.radius_auth() and not public %} {%- if port.radius_auth() %}
aaa port-access mac-based {{ port|int }} aaa port-access mac-based {{ port|int }}
aaa port-access mac-based {{ port|int }} addr-limit {{ port.num_mac() }} aaa port-access mac-based {{ port|int }} addr-limit {{ port.num_mac() }}
aaa port-access mac-based {{ port|int }} logoff-period 3600 aaa port-access mac-based {{ port|int }} logoff-period 3600

View file

@ -3,6 +3,8 @@
""" """
Génération de la configuration d'un switch. Génération de la configuration d'un switch.
Attention, cette version n'a pas encore été totalement testée.
procédure de configuration initiale : procédure de configuration initiale :
* mot de passe admin (password manager user-name <username>) * mot de passe admin (password manager user-name <username>)
* activation du ssh (crypto key generate ssh) * activation du ssh (crypto key generate ssh)
@ -441,11 +443,7 @@ def format_prises_group(data, first, last):
def pretty_print(hostname): def pretty_print(hostname):
"""Affiche joliement le plan de connexion d'un switch""" """Affiche joliement le plan de connexion d'un switch"""
bat, sw_num = get_bat_num(hostname) bat, sw_num = get_bat_num(hostname)
switch = ldap.search(u'host=bat%s-%d.adm.crans.org' % (bat, sw_num))[0]
try:
switch = ldap.search(u'host=bat%s-%d.adm.crans.org' % (bat, sw_num))[0]
except IndexError:
switch = ldap.search(u'host=bat%s-%d.crans.org' % (bat, sw_num))[0]
port_dict = get_port_dict(switch) port_dict = get_port_dict(switch)
total = max(port_dict.keys()) total = max(port_dict.keys())
@ -465,11 +463,7 @@ def conf_switch(hostname):
"""Affiche la configuration d'un switch""" """Affiche la configuration d'un switch"""
bat, sw_num = get_bat_num(hostname) bat, sw_num = get_bat_num(hostname)
try: switch = ldap.search(u'host=bat%s-%d.adm.crans.org' % (bat, sw_num))[0]
switch = ldap.search(u'host=bat%s-%d.adm.crans.org' % (bat, sw_num))[0]
except IndexError:
switch = ldap.search(u'host=bat%s-%d.crans.org' % (bat, sw_num))[0]
tpl_env = jinja2.Environment(loader=jinja2.FileSystemLoader(os.path.dirname(__file__))) tpl_env = jinja2.Environment(loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
##for info: ##for info:
@ -488,6 +482,9 @@ def conf_switch(hostname):
], ],
'radius_key': secrets.get('radius_key'), 'radius_key': secrets.get('radius_key'),
'ntp_servers': ['10.231.136.98'],
'log_servers': ['10.231.136.38'],
# dhcp et isc (secondaire) sont les deux seuls serveurs # dhcp et isc (secondaire) sont les deux seuls serveurs
'dhcp_rid_servers': [34, 160], 'dhcp_rid_servers': [34, 160],
@ -523,24 +520,6 @@ def conf_switch(hostname):
first = netaddr.IPNetwork(net_of_vlan_name(vname)[0]).first first = netaddr.IPNetwork(net_of_vlan_name(vname)[0]).first
data['dhcp_servers'].append(str(netaddr.IPAddress(first + rid))) data['dhcp_servers'].append(str(netaddr.IPAddress(first + rid)))
# Si le switch n'est pas en .adm, il n'est pas publique (ex : batk-0)
# (désactivation de radius etc)
# On règle les logs, ntp, suivant si le switch est public ou privé (adm)
if u"adm" in unicode(switch['host']):
data['public'] = False
data['ntp_servers'] = ['10.231.136.98']
data['log_servers'] = ['10.231.136.38']
data['gateway'] = '10.231.136.4'
data['network_id'] = '10.231.136.0'
data['subnet'] = '255.255.255.0'
else:
data['public'] = True
data['ntp_servers'] = ['138.231.136.98']
data['log_servers'] = ['138.231.136.38']
data['gateway'] = '138.231.136.4'
data['network_id'] = '138.231.136.0'
data['subnet'] = '255.255.248.0'
# Ra gards ne concerne que les 2620 # Ra gards ne concerne que les 2620
if "2620" in switch['info'][0].value: if "2620" in switch['info'][0].value:
data['ra_filter'] = True data['ra_filter'] = True
@ -569,14 +548,9 @@ def conf_switch(hostname):
V_NO: 'no'}[assign] V_NO: 'no'}[assign]
vlan.setdefault(attr, PortList()) vlan.setdefault(attr, PortList())
vlan[attr].extend(p) vlan[attr].extend(p)
if name == 'adm' and not data['public']: if name == 'adm':
vlan['ip_cfg'] = (gethostbyname(hostname), '255.255.255.0') vlan['ip_cfg'] = (gethostbyname(hostname), '255.255.255.0')
if name == 'adherent': if name == 'adherent':
# TODO : proprifier cela
# Si le switch est publique, adh en non tagué partout
if data['public']:
vlan['untagged'] = u'1-' + unicode(switch['nombrePrises'][0])
vlan['ip_cfg'] = (gethostbyname(hostname), '255.255.248.0')
# igmp snooping (multicast) mais nous ne sommes pas querier # igmp snooping (multicast) mais nous ne sommes pas querier
vlan['extra'] = 'ip igmp\nno ip igmp querier' vlan['extra'] = 'ip igmp\nno ip igmp querier'
vlans[name] = vlan vlans[name] = vlan

View file

@ -29,10 +29,8 @@ from whos import aff
import signal import signal
import getopt import getopt
from time import strftime, strptime, localtime, mktime, time from time import strftime, strptime, localtime, mktime, time
import datetime
from dateutil.relativedelta import relativedelta
import re import re
import subprocess
import affich_tools import affich_tools
import config import config
import config.cotisation as cotisation import config.cotisation as cotisation
@ -41,7 +39,7 @@ from lock import make_lock, remove_lock
from ldap_crans import crans_ldap, blacklist_items, droits_possibles, droits_critiques, smtpserv, script_utilisateur from ldap_crans import crans_ldap, blacklist_items, droits_possibles, droits_critiques, smtpserv, script_utilisateur
from ldap_crans import Adherent, AssociationCrans, Club, Facture from ldap_crans import Adherent, AssociationCrans, Club, Facture
from ldap_crans import Machine, MachineFixe, MachineWifi, MachineCrans, BorneWifi from ldap_crans import Machine, MachineFixe, MachineWifi, MachineCrans, BorneWifi
from ldap_crans import tz, generalizedTimeFormat, fromGeneralizedTimeFormat, datetimeFromGTF, datetimeToGTF, localizedDatetime from ldap_crans import tz, generalizedTimeFormat, fromGeneralizedTimeFormat
import user_tests import user_tests
isadm = user_tests.isadm() isadm = user_tests.isadm()
@ -54,8 +52,6 @@ iscontroleur = u'Tresorier' in droits
isbureau = u'Bureau' in droits isbureau = u'Bureau' in droits
encoding = sys.stdin.encoding or 'UTF-8' encoding = sys.stdin.encoding or 'UTF-8'
NAISSANCE_RE = re.compile(r"(?P<jour>[^ ]*)/(?P<mois>[^ ]*)/(?P<annee>[^ ]*)")
if u'Nounou' in droits: if u'Nounou' in droits:
# Si on est nounou # Si on est nounou
if os.path.exists(os.path.expanduser('~/.dialogrc')): if os.path.exists(os.path.expanduser('~/.dialogrc')):
@ -105,30 +101,22 @@ def set_bases(adher):
arg += u'"Chambre :" 4 1 "%s" 4 11 05 00 ' % adher.chbre() arg += u'"Chambre :" 4 1 "%s" 4 11 05 00 ' % adher.chbre()
arg += u'"(bat+numéro)" 4 17 "" 0 0 0 0 ' arg += u'"(bat+numéro)" 4 17 "" 0 0 0 0 '
arg += u'"EXT pour chambre extérieure au campus" 5 1 "" 0 0 0 0 ' arg += u'"EXT pour chambre extérieure au campus" 5 1 "" 0 0 0 0 '
arg += u'"Date de naissance : " 6 1 "" 6 21 11 11 '
arg += u'"Format : dd/mm/yyyy" 7 1 "" 0 0 0 0 '
# Affichage # Affichage
annul, result = dialog(arg) annul, result = dialog(arg)
if annul or any([result[i] == '' for i in xrange(len(result))]): if annul:
return 1 return 1
# Traitement # Traitement
err = '' err = ''
try: try: adher.nom(result[0])
adher.nom(result[0]) except ValueError, c: err += c.args[0] + '\n'
except ValueError, c:
err += c.args[0] + '\n'
try: try: adher.prenom(result[1])
adher.prenom(result[1]) except ValueError, c: err += c.args[0] + '\n'
except ValueError, c:
err += c.args[0] + '\n'
try: try: adher.tel(result[2])
adher.tel(result[2]) except ValueError, c: err += c.args[0] + '\n'
except ValueError, c:
err += c.args[0] + '\n'
# Un adhérent du même nom existe-t-il déjà ? # Un adhérent du même nom existe-t-il déjà ?
req = 'nom=' + result[0] + '&prenom=' + result[1] req = 'nom=' + result[0] + '&prenom=' + result[1]
@ -140,25 +128,6 @@ def set_bases(adher):
if no: if no:
return 1 return 1
# On controle que l'adh est majeur
naissance = NAISSANCE_RE.match(result[4].decode(config.in_encoding))
if naissance is None:
err += "La date est invalide"
else:
naissance = naissance.groupdict()
try:
naissance_date = datetime.date(int(naissance['annee']), int(naissance['mois']), int(naissance['jour']))
age = relativedelta(datetime.date.today(), naissance_date).years
if age < 18:
arg = u'--title "Inscription adhérent" '
arg += u'--yesno "Cet adhérent est mineur, merci de demander un accord écrit des parents'
arg += u'\nContinuer ?" 0 0'
no, res = dialog(arg)
if no:
return 1
except ValueError, c:
err += c.args[0] + '\n'
err += _set_chbre(adher, result[3]) err += _set_chbre(adher, result[3])
# Des erreurs ? # Des erreurs ?
@ -244,7 +213,6 @@ def set_etudes(adher):
arg += u'"Maximilien Sorre" "" ' arg += u'"Maximilien Sorre" "" '
arg += u'"Gustave Eiffel" "" ' arg += u'"Gustave Eiffel" "" '
arg += u'"EFREI" "" ' arg += u'"EFREI" "" '
arg += u'"ESIGETEL" "" '
arg += u'"ESTP" "" ' arg += u'"ESTP" "" '
arg += u'"P1" "Université Panthéon Sorbonne" ' arg += u'"P1" "Université Panthéon Sorbonne" '
arg += u'"P2" "Université Panthéon Assas" ' arg += u'"P2" "Université Panthéon Assas" '
@ -456,12 +424,9 @@ def set_contact(adher):
if result[0].split()[0] == 'Laisser': if result[0].split()[0] == 'Laisser':
break break
elif result[0].split()[1] == u'un': elif result[0].split()[1] == u'un':
if not set_compte(adher): if not set_compte(adher): break
set_mail_ext(adher)
break
else: else:
if not set_mail(adher): if not set_mail(adher): break
break
def set_mail(adher): def set_mail(adher):
"""Demande l'adresse mail extérieure d'un adhérent """Demande l'adresse mail extérieure d'un adhérent
@ -829,13 +794,7 @@ def del_adher(adher):
arg += u'--msgbox "Le commentaire est obligatoire\n\n\n" 0 0' arg += u'--msgbox "Le commentaire est obligatoire\n\n\n" 0 0'
dialog(arg) dialog(arg)
try: adher.delete(res[0])
adher.delete(res[0])
except EnvironmentError, c:
arg = u'--title "Destruction du compte" '
arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
dialog(arg)
return
arg = u'--title "Destruction adhérent" ' arg = u'--title "Destruction adhérent" '
arg += u'--msgbox "Adhérent détruit\n\n\n" 0 0' arg += u'--msgbox "Adhérent détruit\n\n\n" 0 0'
@ -1090,18 +1049,18 @@ def set_solde(clas):
annul, comment = dialog(arg) annul, comment = dialog(arg)
if not annul: if not annul:
if comment: if comment[0]:
comment = comment[0] comment = comment[0]
else: else:
comment = '' comment = None
f = Facture(clas) f = Facture(clas)
f.ajoute({'nombre': 1, 'code':'SOLDE', 'designation': "Modification du solde par un imprimeur. Moyen de paiement: %s, remarque: %s" % (_mode, comment.decode(config.in_encoding)), 'pu': _montant}) f.ajoute({'nombre': 1, 'code':'SOLDE', 'designation': "Modification du solde par un imprimeur. Moyen de paiement: %s, remarque: %s" % (_mode, comment), 'pu': _montant})
f.modePaiement(_mode.lower()) f.modePaiement(_mode.lower())
try: try:
# Met aussi à jour le solde. # Met aussi à jour le solde.
f.recuPaiement(datetimeToGTF(localizedDatetime())) f.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
f.save() f.save()
db.services_to_restart('mail_solde', [ db.services_to_restart('mail_solde', [
'%s a fait %s euros pour %s [mode: %s, remarque: %s]' % '%s a fait %s euros pour %s [mode: %s, remarque: %s]' %
@ -1115,7 +1074,6 @@ def set_solde(clas):
arg = u'--title "%s du solde de %s" ' % (_kword, clas.Nom()) arg = u'--title "%s du solde de %s" ' % (_kword, clas.Nom())
arg += u'--msgbox "Modification effectuée, merci de noter le numéro de facture %s." 0 0' % (f.numero(),) arg += u'--msgbox "Modification effectuée, merci de noter le numéro de facture %s." 0 0' % (f.numero(),)
dialog(arg) dialog(arg)
break
def set_vente(proprio): def set_vente(proprio):
u""" u"""
@ -1203,7 +1161,6 @@ def set_vente(proprio):
menu.append(u'"Spc" "Espèces" ') menu.append(u'"Spc" "Espèces" ')
menu.append(u'"Chq" "Chèque" ') menu.append(u'"Chq" "Chèque" ')
menu.append(u'"Cb" "Carte bancaire" ') menu.append(u'"Cb" "Carte bancaire" ')
menu.append(u'"Note" "Note Kfet (attention, moins traçable)" ')
if isimprimeur and proprio.solde() - f.total() > 0: if isimprimeur and proprio.solde() - f.total() > 0:
menu.append(u'"Sol" "Solde Crans (actuel : %s€)" ' % (proprio.solde())) menu.append(u'"Sol" "Solde Crans (actuel : %s€)" ' % (proprio.solde()))
@ -1230,10 +1187,6 @@ def set_vente(proprio):
f.modePaiement('cheque') f.modePaiement('cheque')
paiement = u"Chèque" paiement = u"Chèque"
annul, comment = dialog(arg) annul, comment = dialog(arg)
elif result[0] == "Note":
f.modePaiement('note')
paiement = u"Note"
annul, comment = dialog(arg)
elif result[0] == "Sol" and isimprimeur: elif result[0] == "Sol" and isimprimeur:
f.modePaiement('solde') f.modePaiement('solde')
paiement = u"Solde Crans" paiement = u"Solde Crans"
@ -1257,15 +1210,8 @@ def set_vente(proprio):
return 1 return 1
else: else:
try: try:
f.recuPaiement(datetimeToGTF(localizedDatetime())) f.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
f.save() f.save()
# arg = u'--title "Impression facture" '
# arg += u'--yesno "Voulez vous imprimer cette facture ?\n" 0 0'
# no, res_1 = dialog(arg)
# if no:
# return 1
# else:
# subprocess.call(['/usr/scripts/cransticket/dump_creds.py','fid=%s' % f.numero()])
arg = u'--title "Vente terminée" ' arg = u'--title "Vente terminée" '
arg += u'--msgbox "Vous pouvez remettre à l\'adherent les articles suivant :\n%s\n\nMerci de noter la facture: fid=%s" 0 0' % ('\n'.join([ arg += u'--msgbox "Vous pouvez remettre à l\'adherent les articles suivant :\n%s\n\nMerci de noter la facture: fid=%s" 0 0' % ('\n'.join([
"%s %s" % (art['nombre'], art['designation']) "%s %s" % (art['nombre'], art['designation'])
@ -1304,26 +1250,9 @@ def confirm(clas):
return 1 return 1
try: try:
res = clas.save() res = clas.save()
cprint(res)
affich_tools.prompt(u"Appuyez sur ENTREE pour continuer")
if isinstance(clas, MachineWifi):
arg = u'--title "Imprimer code wifi ?" '
arg += u'--yesno "Voulez vous imprimer ce code wifi ?\n" 0 0'
no, res_0 = dialog(arg)
if no:
pass
else:
subprocess.call(['/usr/scripts/cransticket/dump_creds.py', 'mid=%s' % clas.id()])
if in_facture is not None: if in_facture is not None:
in_facture.recuPaiement(datetimeToGTF(localizedDatetime())) in_facture.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
in_facture.save() in_facture.save()
# arg = u'--title "Impression facture" '
# arg += u'--yesno "Voulez vous imprimer cette facture ?\n" 0 0'
# no, res_2 = dialog(arg)
# if no:
# pass
# else:
# subprocess.call(['/usr/scripts/cransticket/dump_creds.py','fid=%s' % in_facture.numero()])
except Exception as c: except Exception as c:
arg = u'--title "Enregistrement" ' arg = u'--title "Enregistrement" '
arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(unicode(c.args[0])) arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(unicode(c.args[0]))
@ -1331,6 +1260,9 @@ def confirm(clas):
return 1 return 1
in_facture = None in_facture = None
cprint(res)
affich_tools.prompt(u"Appuyez sur ENTREE pour continuer")
def set_blackliste(clas): def set_blackliste(clas):
u""" Édite ou ajoute un item de la blackliste """ u""" Édite ou ajoute un item de la blackliste """
bl = clas.blacklist() bl = clas.blacklist()
@ -1562,9 +1494,6 @@ def set_adhesion(proprio):
facture.modePaiement(_mode.lower()) facture.modePaiement(_mode.lower())
break break
in_facture = facture in_facture = facture
if not in_facture._data.get('finConnexion', []) and not in_facture._data.get('finAdhesion', []):
in_facture = None
proprio.restore()
def set_connexion(proprio): def set_connexion(proprio):
"""Maj de la période d'accès de l'adhérent""" """Maj de la période d'accès de l'adhérent"""
@ -1572,16 +1501,12 @@ def set_connexion(proprio):
# Si l'adhérent ne l'est plus, on commence par le faire adhérer, sauf s'il a une facture adhésion. # Si l'adhérent ne l'est plus, on commence par le faire adhérer, sauf s'il a une facture adhésion.
adhEnd = proprio.adhesion() adhEnd = proprio.adhesion()
if in_facture is not None: if in_facture is not None:
adhEnd = max(adhEnd, fromGeneralizedTimeFormat(in_facture._data.get('finAdhesion', ["19700101000000Z"])[0])) adhEnd = max(adhEnd, fromGeneralizedTimeFormat(in_facture._data.get('finAdhesion', ["19700101000000Z"])[0]))
if adhEnd < time():
if adhEnd - cotisation.delai_readh < time():
stat = set_adhesion(proprio) stat = set_adhesion(proprio)
if stat == 1:
if stat == 1 and adhEnd < time():
return 1 return 1
if in_facture is not None: if in_facture is not None:
adhEnd = max(adhEnd, fromGeneralizedTimeFormat(in_facture._data.get('finAdhesion', ["19700101000000Z"])[0])) adhEnd = max(adhEnd, fromGeneralizedTimeFormat(in_facture._data.get('finAdhesion', ["19700101000000Z"])[0]))
@ -1596,7 +1521,7 @@ def set_connexion(proprio):
while True: while True:
args = u'--title "Connexion de %s" ' % proprio.Nom() args = u'--title "Connexion de %s" ' % proprio.Nom()
if proprio.connexion() > time(): if proprio.connexion() > time():
args += u'--menu "Connexion jusqu\'au %s, choisir une durée de prolongation. : " 0 0 0 ' % (strftime("%d/%m/%Y %H:%M:%S", localtime(proprio.connexion())),) args += u'--menu "Connexion jusqu\'au %s, choisir une durée de prolongation. : " 0 0 0 ' % (strftime("%d/%m/%Y %H:%M:%S"),)
else: else:
args += u'--menu "Connexion actuellement inactive, choisir une durée. : " 0 0 0 ' args += u'--menu "Connexion actuellement inactive, choisir une durée. : " 0 0 0 '
args += u'"An" "Prolonger d\'un an." ' args += u'"An" "Prolonger d\'un an." '
@ -1606,8 +1531,7 @@ def set_connexion(proprio):
annul, res = dialog(args) annul, res = dialog(args)
if annul: if annul:
if in_facture is not None: in_facture.supprime(pop=True)
in_facture.supprime(pop=True)
return 1 return 1
res = res[0] res = res[0]
if res == "An": if res == "An":
@ -1657,17 +1581,17 @@ def set_connexion(proprio):
newEnd = fromGeneralizedTimeFormat(facture._data.get('finConnexion', ["19700101000000Z"])[0]) newEnd = fromGeneralizedTimeFormat(facture._data.get('finConnexion', ["19700101000000Z"])[0])
if newEnd > adhEnd: if newEnd > adhEnd:
arg = u'--title "Avertissement" ' arg = u'--title "Avertissement" '
arg += u'--yesno "La nouvelle fin de connexion (%s) arriverait après la fin de l\'adhésion actuelle (%s).\nIl sera nécessaire que l\'adhérent réadhère, (possible %s jours avant la fin de l\'adhésion actuelle).\n\nLe paiement ne vaut *PAS* réadhésion. Merci de lui préciser explicitement !" 0 0 ' % (strftime("%d/%m/%Y %H:%M:%S", localtime(newEnd)), strftime("%d/%m/%Y %H:%M:%S", localtime(adhEnd)), cotisation.delai_readh_jour) arg += u'--yesno "La fin de la connexion de l\'adhérent (%s) tombera après la fin de son adhésion (%s).\nS\'il veut en profiter, il lui faudra éventuellement réadhérer. Continuer ?" 0 0' %(strftime('%d/%m/%Y %H:%M:%S', localtime(newEnd)), strftime('%d/%m/%Y %H:%M:%S', localtime(adhEnd)), )
annul, res = dialog(arg) no, res = dialog(arg)
if annul: if no:
facture._set('finConnexion', []) facture._set('finConnexion', [])
facture._set('debutConnexion', []) facture._set('debutConnexion', [])
facture.supprime(pop=True) facture.supprime(pop=True)
break continue
if not facture.modePaiement(): if not facture.modePaiement():
arg = u'--title "Mode de paiement pour la connexion de %s" ' % (proprio.Nom(),) arg = u'--title "Mode de paiement pour la connexion de %s" ' % (proprio.Nom(),)
arg += u'--menu "Comment %s souhaite-t-il payer ?" 0 0 0 ' % (proprio.Nom(),) arg += u'--menu "Comment %s souhaite-t-il payer ?" 0 0 0 ' % (proprio.Nom(), )
arg += u'"Liquide" "En espèces : penser à mettre l\'argent dans une enveloppe." ' arg += u'"Liquide" "En espèces : penser à mettre l\'argent dans une enveloppe." '
arg += u'"Cheque" "Par chèque : ne pas oublier de vérifier signature, date, ordre et montant." ' arg += u'"Cheque" "Par chèque : ne pas oublier de vérifier signature, date, ordre et montant." '
arg += u'"Carte" "Par CB : tromboner le ticket." ' arg += u'"Carte" "Par CB : tromboner le ticket." '
@ -1694,10 +1618,6 @@ def set_connexion(proprio):
break break
in_facture = facture in_facture = facture
if not in_facture._data.get('finConnexion', []) and not in_facture._data.get('finAdhesion', []):
in_facture = None
proprio.restore()
############################################################### ###############################################################
## Fonctions de remplissage ou modification des paramètres club ## Fonctions de remplissage ou modification des paramètres club
@ -1858,7 +1778,7 @@ def set_facture_recu(facture):
if annul: if annul:
return 1 return 1
if u"Pmt" in res: if u"Pmt" in res:
facture.recuPaiement(datetimeToGTF(localizedDatetime())) facture.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
else: else:
facture.recuPaiement(False) facture.recuPaiement(False)
@ -2184,10 +2104,8 @@ def new_adher(adher):
4 etapes : 4 etapes :
* set_bases * set_bases
* set_etudes * set_etudes
* set_adhesion * set_admin
* set_connexion
* set_contact * set_contact
(qui appelle set_mail_ext si on met un compte crans)
* set_rque * set_rque
Retourne 1 si annulation. Retourne 1 si annulation.
""" """
@ -2199,16 +2117,15 @@ def new_adher(adher):
set_etudes, set_etudes,
set_adhesion, set_adhesion,
set_connexion, set_connexion,
set_admin,
set_contact, set_contact,
set_mail_ext,
set_rque, set_rque,
] ]
step = 0 step = 0
while step < len(steps): while step < len(steps):
if steps[step](adher): if steps[step](adher): step -= 1
if step == 0:
return 1
step -= 1
else: step += 1 else: step += 1
if not confirm(adher): break if not confirm(adher): break
@ -2238,8 +2155,7 @@ def modif_adher(adher):
arg += u'--menu "Que souhaitez vous modifier ?" 0 0 0 ' arg += u'--menu "Que souhaitez vous modifier ?" 0 0 0 '
arg += u'"Connexion" "Mise à jour de l\'accès Internet (effectue la réadhésion si besoin)" ' arg += u'"Connexion" "Mise à jour de l\'accès Internet (effectue la réadhésion si besoin)" '
arg += u'"Adhesion" "Pour toute réadhésion *sans* connexion." ' arg += u'"Adhesion" "Pour toute réadhésion *sans* connexion." '
if isadm or isbureau: arg += u'"Administratif" "Pour renseigner la fournitire de la charte des MA." '
arg += u'"Administratif" "Pour renseigner la fourniture de la charte des MA." '
arg += u'"Etat-civil" "Nom, prénom" ' arg += u'"Etat-civil" "Nom, prénom" '
if adher.chbre() == 'EXT': if adher.chbre() == 'EXT':
arg += u'"Adresse" "Déménagement" ' arg += u'"Adresse" "Déménagement" '
@ -2251,7 +2167,7 @@ def modif_adher(adher):
arg += u'"Mail" "Créer un compte ou changer l\'adresse mail de contact" ' arg += u'"Mail" "Créer un compte ou changer l\'adresse mail de contact" '
if 'cransAccount' in adher._data['objectClass']: if 'cransAccount' in adher._data['objectClass']:
arg += u'"MailExt" "Ajouter une adresse mail de contact extérieur." ' arg += u'"MailExt" "Ajouter une adresse mail de contact extérieur." '
arg += u'"Alias" "Créer ou supprimer un alias mail" ' arg += u'"Alias" "Créer ou supprimer un alias mail" '
arg += u'"GPGFingerprint" "Ajouter ou supprimer une empreinte GPG" ' arg += u'"GPGFingerprint" "Ajouter ou supprimer une empreinte GPG" '
arg += u'"Remarque" "Ajouter ou modifer un commentaire" ' arg += u'"Remarque" "Ajouter ou modifer un commentaire" '
if isadm or isbureau: if isadm or isbureau:
@ -2263,7 +2179,7 @@ def modif_adher(adher):
arg += u'"Shell" "Changer le shell de cet utilisateur" ' arg += u'"Shell" "Changer le shell de cet utilisateur" '
if isdeconnecteur: if isdeconnecteur:
arg += u'"Blackliste" "Modifier la blackliste de cet adhérent" ' arg += u'"Blackliste" "Modifier la blackliste de cet adhérent" '
if isimprimeur and adher.compte(): if isimprimeur:
arg += u'"Solde" "Effectuer un débit/crédit pour cet adhérent" ' arg += u'"Solde" "Effectuer un débit/crédit pour cet adhérent" '
arg += u'"Vente" "Vendre un cable ou adaptateur ethernet ou autre" ' arg += u'"Vente" "Vendre un cable ou adaptateur ethernet ou autre" '
@ -2320,15 +2236,13 @@ def modif_adher(adher):
arg += u'--msgbox "Vous n\'avez pas les droits necessaires pour effectuer cette opération.\n\n\n" 0 0' arg += u'--msgbox "Vous n\'avez pas les droits necessaires pour effectuer cette opération.\n\n\n" 0 0'
dialog(arg) dialog(arg)
return modif_adher(adher) return modif_adher(adher)
arg = u'--title "Départ de %s" ' % adher.Nom() arg = u'--title "Départ de %s" ' % adher.Nom()
arg += u'--yesno "Le départ du campus de %s va provoquer la destruction de son compte.\n' % adher.Nom() arg += u'--yesno "Le départ du campus de %s va provoquer la destruction de son compte.\n' % adher.Nom()
arg += u'\nDoit-on continuer ?" 0 0' arg += u'\nDoit-on continuer ?" 0 0'
no, res = dialog(arg) no, res = dialog(arg)
if no: return modif_adher(adher)
if no: for m in adher.machines():
return modif_adher(adher) m.delete("Depart du campus")
try: try:
adher.delete("Depart du campus") adher.delete("Depart du campus")
except EnvironmentError, c: except EnvironmentError, c:

View file

@ -1,19 +1,5 @@
#!/usr/bin/env python2.7 #!/usr/bin/env python2.7
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
import os import os

View file

@ -1,19 +1,5 @@
#!/bin/bash /usr/scripts/python.sh #!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
"""Ce sont les variables utiles pour les autres scripts du """Ce sont les variables utiles pour les autres scripts du
module""" module"""

View file

@ -1,19 +1,5 @@
#!/bin/bash /usr/scripts/python.sh #!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
"""Contient les outils pour manipuler des adresses MAC """Contient les outils pour manipuler des adresses MAC
dans le module hptools""" dans le module hptools"""

View file

@ -1,19 +1,5 @@
#!/bin/bash /usr/scripts/python.sh #!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
"""Contient la définition et les outils pour bosser avec """Contient la définition et les outils pour bosser avec
les ports. les ports.

View file

@ -1,19 +1,5 @@
#!/bin/bash /usr/scripts/python.sh #!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
"""Ce fichier propose un client snmp basique""" """Ce fichier propose un client snmp basique"""
import netsnmp import netsnmp

View file

@ -1,19 +1,5 @@
#!/bin/bash /usr/scripts/python.sh #!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
"""Outils principaux pour la description d'un switch""" """Outils principaux pour la description d'un switch"""
import socket import socket
@ -65,7 +51,7 @@ class HPSwitch(object):
try: try:
netaddr.IPAddress(switch) netaddr.IPAddress(switch)
except netaddr.AddrFormatError: except netaddr.AddrFormatError:
raise SwitchNotFound("Switch %r non trouvé." % (switch,)) raise SwitchNotFound
switch_object = HPSwitchFactory.get_switch(__switch) switch_object = HPSwitchFactory.get_switch(__switch)
if switch_object is None: if switch_object is None:

View file

@ -1,19 +1,5 @@
#!/bin/bash /usr/scripts/python.sh #!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
"""Fournit des outils et fonctions appelables au besoin""" """Fournit des outils et fonctions appelables au besoin"""
from gestion import annuaires_pg from gestion import annuaires_pg
@ -75,16 +61,10 @@ def fetch_all_ports(switch, output):
__stuff = filter_uplink(switch, __stuff) __stuff = filter_uplink(switch, __stuff)
output[switch] = __stuff output[switch] = __stuff
def populate_all_switches(switches=None): def populate_all_switches():
"""Remplit l'ensemble des switches avec les MACS qui sont """Remplit l'ensemble des switches avec les MACS qui sont
présentes sur leurs ports. présentes sur leurs ports"""
switches = annuaires_pg.all_switchs()
Peut également ne remplir qu'une liste spécifique si fournie
en argument."""
if switches == None:
switches = annuaires_pg.all_switchs()
hp_switches = { hp_switches = {
switch : HPSwitch(switch) switch : HPSwitch(switch)
for switch in switches for switch in switches

View file

@ -21,10 +21,10 @@
import sys import sys
import os, re, syslog, cPickle, socket import os, re, syslog, cPickle, socket
from ldap_crans import crans_ldap, hostname, generalizedTimeFormat from ldap_crans import crans_ldap, hostname
from commands import getstatusoutput from commands import getstatusoutput
from config import NETs, role, prefix, rid, output_file, filter_policy, rid_primaires from config import NETs, role, prefix, rid, output_file, filter_policy, rid_primaires
from config import blacklist_sanctions, blacklist_sanctions_soft, blacklist_bridage_upload, file_pickle, periode_transitoire, gtf_debut_periode_transitoire from config import blacklist_sanctions, blacklist_sanctions_soft, blacklist_bridage_upload, file_pickle, ann_scol, periode_transitoire
from iptools import AddrInNet from iptools import AddrInNet
from ridtools import Rid, find_rid_plage from ridtools import Rid, find_rid_plage
import subprocess import subprocess
@ -768,13 +768,9 @@ def blacklist(ipt):
if [x for x in sanctions if x in blacklist_sanctions_ipv6]: if [x for x in sanctions if x in blacklist_sanctions_ipv6]:
blcklst.extend(target.machines()) blcklst.extend(target.machines())
s = db.search('mblacklist=*&finConnexion>=%(fin)s&finAdhesion>=%(fin)s' % { s = db.search('mblacklist=*&paiement=%s' % ann_scol)
'fin': generalizedTimeFormat(),
})
if periode_transitoire: if periode_transitoire:
s['machine'].extend(db.search('mblacklist=*&finConnexion>=%(fin)s&finAdhsion>=%(fin)s' % { s['machine'].extend(db.search('mblacklist=*&paiement=%s' % (ann_scol-1))['machine'])
'fin': gtf_debut_periode_transitoire,
})['machine'])
for target in s['machine']: for target in s['machine']:
sanctions = target.blacklist_actif() sanctions = target.blacklist_actif()

View file

@ -26,7 +26,7 @@ import ldap.modlist
import ldap_passwd import ldap_passwd
import netaddr import netaddr
import traceback import traceback
import subprocess
import annuaires_pg as annuaires import annuaires_pg as annuaires
import config import config
import config.impression import config.impression
@ -47,16 +47,6 @@ import ridtools
from user_tests import isadm from user_tests import isadm
import getpass import getpass
try:
import pytz
except:
pytz = None
try:
import dateutil.tz
except:
dateutil = None
cur_user = os.getenv("SUDO_USER") or os.getenv("USER") or os.getenv("LOGNAME") or getpass.getuser() cur_user = os.getenv("SUDO_USER") or os.getenv("USER") or os.getenv("LOGNAME") or getpass.getuser()
date_format = '%d/%m/%Y %H:%M' date_format = '%d/%m/%Y %H:%M'
@ -119,7 +109,7 @@ blacklist_items = config.blacklist_items
### Droits possibles ### Droits possibles
droits_possibles = [u'Multimachines', u'Cableur', u'Imprimeur', u'Apprenti', droits_possibles = [u'Multimachines', u'Cableur', u'Imprimeur', u'Apprenti',
u'Webmaster', u'Moderateur', u'Webradio', u'Webmaster', u'Moderateur', u'Webradio',
u'Nounou', u'Tresorier', u'Bureau'] u'Nounou', u'Tresorier', u'Bureau', u'Troll']
################################################################################## ##################################################################################
### Droits critiques, ie que seules les nounous peuvent attribuer ### Droits critiques, ie que seules les nounous peuvent attribuer
@ -134,6 +124,7 @@ droits_vieux = [u'Nounou', u'Bureau']
### Variables internes diverses ### Variables internes diverses
#isadm = user_tests.isadm() #isadm = user_tests.isadm()
#isdeconnecteur = user_tests.isdeconnecteur() #isdeconnecteur = user_tests.isdeconnecteur()
ann_scol = config.ann_scol
#script_utilisateur = user_tests.getuser() #script_utilisateur = user_tests.getuser()
script_utilisateur = cur_user script_utilisateur = cur_user
@ -158,15 +149,12 @@ def tz(thetz):
else: else:
return "%s%04d" % ("+"*(thetz < 0) + "-"*(thetz > 0), abstz) return "%s%04d" % ("+"*(thetz < 0) + "-"*(thetz > 0), abstz)
def generalizedTimeFormat(stamp=None): def generalizedTimeFormat(stamp):
"""Converts a timestamp (local) in a generalized time format """Converts a timestamp (local) in a generalized time format
for LDAP for LDAP
""" """
if stamp is None:
stamp = time.time()
return "%s%s" % (time.strftime("%Y%m%d%H%M%S", time.localtime(stamp)), tz(time.altzone/3600)) return "%s%s" % (time.strftime("%Y%m%d%H%M%S", time.localtime(stamp)), tz(time.altzone/3600))
def fromGeneralizedTimeFormat(gtf): def fromGeneralizedTimeFormat(gtf):
@ -175,60 +163,6 @@ def fromGeneralizedTimeFormat(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 datetimeFromGTF(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'
return localizedDatetime(date, tz)
def datetimeToGTF(datetime_obj):
"""Transforms a datetime to a GTF"""
to_append = ""
if datetime_obj.utcoffset() is None:
if pytz is not None:
datetime_obj = pytz.utc.localize(datetime_obj)
else:
to_append = "Z"
mostly_gtf = datetime.datetime.strftime(datetime_obj, "%Y%m%d%H%M%S%z")
return mostly_gtf.replace('+0000', "Z") + to_append
def localizedDatetime(date=None, tz=None):
"""Génère un datetime localisé à partir d'une chaîne de la forme
%Y%m%d%H%M%S, et d'une chaîne tz de la forme +0200"""
_notz = (tz is None)
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 strip_accents(a, sois_un_porc_avec_les_espaces = True): def strip_accents(a, sois_un_porc_avec_les_espaces = True):
""" Supression des accents de la chaîne fournie """ """ Supression des accents de la chaîne fournie """
res = normalize('NFKD', decode(a)).encode('ASCII', 'ignore') res = normalize('NFKD', decode(a)).encode('ASCII', 'ignore')
@ -409,7 +343,7 @@ class Service:
starting = self.start starting = self.start
starting.sort() starting.sort()
dates = u' et '.join(map(lambda t: t < time.time() and \ dates = u' et '.join(map(lambda t: t < time.time() and \
u"maintenant" or time.strftime(date_format_new, u"maintenant" or time.strftime(date_format,
time.localtime(t)), time.localtime(t)),
self.start)) self.start))
dates = u" à partir d%s %s" % (dates.startswith(u"maintenant") and u"e" or u"u", dates = u" à partir d%s %s" % (dates.startswith(u"maintenant") and u"e" or u"u",
@ -804,7 +738,7 @@ class CransLdap:
result[i] = [] result[i] = []
# Fonction utile # Fonction utile
def build_filtre(champ, expr, neg=False, comp=''): def build_filtre(champ, expr, neg=False):
""" """
Retourne une chaine pour recherche dans la base LDAP Retourne une chaine pour recherche dans la base LDAP
du style (champ=expr) en adaptant les valeurs de expr au champ. du style (champ=expr) en adaptant les valeurs de expr au champ.
@ -835,20 +769,16 @@ class CransLdap:
# définifif (cf config.py). # définifif (cf config.py).
if config.periode_transitoire: if config.periode_transitoire:
# Pour la période transitoire année précédente ok # Pour la période transitoire année précédente ok
el = "(&(finAdhesion>=%(fin)s)(finConnexion>=%(fin)s))" % { el = "(|(paiement=%d)(paiement=%d)(finAdhesion>=%s))" % (config.ann_scol, config.ann_scol-1, generalizedTimeFormat(time.time()))
'fin': config.gtf_debut_periode_transitoire,
}
else: else:
el = "(&(finAdhesion>=%(fin)s)(finConnexion>=%(fin)s))" % { el = "(|(paiement=%s)(finAdhesion>=%s))" % (config.ann_scol, generalizedTimeFormat(time.time()))
'fin': generalizedTimeFormat(),
}
# Doit-on bloquer en cas de manque de la carte d'etudiant ? # Doit-on bloquer en cas de manque de la carte d'etudiant ?
# (si période transitoire on ne bloque dans aucun cas) # (si période transitoire on ne bloque dans aucun cas)
elif champ[1:] == 'blacklist': elif champ[1:] == 'blacklist':
el = '(blacklist=%s)' % expr el = '(blacklist=%s)' % expr
else: else:
# Cas général # Cas général
el = '(%s%s=%s)' % (champ, comp, expr) el = '(%s=%s)' % (champ, expr)
if neg: el = '(!%s)' % el if neg: el = '(!%s)' % el
return el return el
@ -871,16 +801,12 @@ class CransLdap:
# Test de l'expression de recherche et classement par filtres # Test de l'expression de recherche et classement par filtres
for cond in conds: for cond in conds:
neg = False neg = False
comp = ''
try: try:
champ, expr = cond.strip().split('=') champ, expr = cond.strip().split('=')
if champ[-1] == '!': if champ[-1] == '!':
# Négation pour ce champ # Négation pour ce champ
champ = champ[:-1] champ = champ[:-1]
neg = True neg = True
if champ[-1] in ['>', '<']:
comp = champ[-1]
champ = champ[0:-1]
except: except:
raise ValueError(u'Syntaxe de recherche invalide (%s)' % cond) raise ValueError(u'Syntaxe de recherche invalide (%s)' % cond)
@ -900,7 +826,7 @@ class CransLdap:
# Construction du filtre # Construction du filtre
for i in filtres: for i in filtres:
if champ in self.search_champs[i]: if champ in self.search_champs[i]:
filtre[i] += build_filtre(champ, expr, neg, comp) filtre[i] += build_filtre(champ, expr, neg)
ok = True ok = True
if champ not in self.auto_search_machines_champs \ if champ not in self.auto_search_machines_champs \
and champ not in self.non_auto_search_machines_champs: and champ not in self.non_auto_search_machines_champs:
@ -1072,6 +998,9 @@ class BaseClasseCrans(CransLdap):
def connexion(self, update=False, f=None): def connexion(self, update=False, f=None):
return 0.0 return 0.0
def sursis_carte(self):
return False
def chbre(self, new=None): def chbre(self, new=None):
return "????" return "????"
@ -1298,7 +1227,7 @@ class BaseClasseCrans(CransLdap):
# Cas spécial # Cas spécial
if "solde" in self.modifs: if "solde" in self.modifs:
diff = round(float(self._init_data.get('solde', [0])[0]) - float(self._data.get('solde', [0])[0]), 2) diff = float(self._init_data.get('solde', [0])[0]) - float(self._data.get('solde', [0])[0])
if diff > 0: if diff > 0:
modif['solde'] = "debit %s Euros" % str(diff) modif['solde'] = "debit %s Euros" % str(diff)
else: else:
@ -1360,7 +1289,7 @@ class BaseClasseCrans(CransLdap):
modif = ', '.join(liste_historique) modif = ', '.join(liste_historique)
timestamp = time.localtime() timestamp = time.localtime()
hist = "%s, %s" % ( time.strftime(date_format_new, timestamp), script_utilisateur ) hist = "%s, %s" % ( time.strftime(date_format, timestamp), script_utilisateur )
if self.modifs.has_key('derniereConnexion'): if self.modifs.has_key('derniereConnexion'):
# On nettoie l'historique pour ne garder que la dernière modification # On nettoie l'historique pour ne garder que la dernière modification
@ -1372,7 +1301,7 @@ class BaseClasseCrans(CransLdap):
# On loggue # On loggue
try: try:
fd = file('%s/%s_%s_%s' % ("%s/logs" % config.cimetiere, str(self.__class__).split('.')[-1], fd = file('%s/%s_%s_%s' % ("%s/logs" % config.cimetiere, str(self.__class__).split('.')[-1],
time.strftime('%Y-%m-%d-%H:%M:%S', timestamp), self.nom()), 'wb') time.strftime('%Y-%m-%d-%H:%M', timestamp), self.nom()), 'wb')
fd.write("%s\n" % self._data) fd.write("%s\n" % self._data)
fd.close() fd.close()
except: except:
@ -1463,12 +1392,12 @@ class BaseClasseCrans(CransLdap):
# Sauvegarde # Sauvegarde
t = str(self.__class__).split('.')[-1] t = str(self.__class__).split('.')[-1]
fd = open('%s/%s/%s_%s' % (config.cimetiere, t, fd = open('%s/%s/%s_%s' % (config.cimetiere, t,
time.strftime('%Y-%m-%d-%H:%M:%S'), time.strftime('%Y-%m-%d-%H:%M'),
self.nom()), 'wb') self.nom()), 'wb')
self.conn = None # Fermeture des connexions à la base sinon cPickle ne marchera pas self.conn = None # Fermeture des connexions à la base sinon cPickle ne marchera pas
cPickle.dump(self, fd, 2) cPickle.dump(self, fd, 2)
fd.close() fd.close()
index = u"%s, %s : %s %s # %s\n" % (time.strftime(date_format_new), index = u"%s, %s : %s %s # %s\n" % (time.strftime(date_format),
script_utilisateur, t, script_utilisateur, t,
self.Nom(), decode(comment)) self.Nom(), decode(comment))
@ -1615,6 +1544,7 @@ class BaseProprietaire(BaseClasseCrans):
finAdh.append(facture._data['finAdhesion'][0]) finAdh.append(facture._data['finAdhesion'][0])
self._set('debutAdhesion', debutAdh) self._set('debutAdhesion', debutAdh)
self._set('finAdhesion', finAdh) self._set('finAdhesion', finAdh)
self._save()
def droits(self, droits=None, light=False): def droits(self, droits=None, light=False):
""" Renvoie les droits courants. Non modifiable (sauf si surchargée dans classe enfant)""" """ Renvoie les droits courants. Non modifiable (sauf si surchargée dans classe enfant)"""
@ -2018,9 +1948,6 @@ class BaseProprietaire(BaseClasseCrans):
def delete(self, comment=''): def delete(self, comment=''):
"""Destruction du propriétaire""" """Destruction du propriétaire"""
if max(self.connexion(), self.adhesion()) + cotisation.del_post_adh >= time.time():
raise EnvironmentError("Vous ne pouvez supprimer un adhérent que %s jours après l'expiration de son adhésion et de sa connexion" % (cotisation.del_post_adh_jours,))
for m in self.machines(): for m in self.machines():
# Destruction machines # Destruction machines
m.delete(comment) m.delete(comment)
@ -2115,11 +2042,9 @@ class BaseProprietaire(BaseClasseCrans):
compte = self._data.get('uid', [''])[0] compte = self._data.get('uid', [''])[0]
if compte: if compte:
ret += u'\nUn compte a été créé :\n login : %s\n' % self.compte() ret += u'\nUn compte a été créé :\n login : %s\n' % self.compte()
r = prompt(u"Attribuer tout de suite un mot de passe, (A pour Automatique) ? [o/n/A]", "A") r = prompt(u"Attribuer tout de suite un mot de passe ? [O/N]", "O")
if r == 'O' or r == 'o': if r == 'O' or r == 'o':
change_password(login=self.compte()) change_password(login=self.compte())
if r == 'A' or r == 'a':
subprocess.call(['/usr/scripts/cransticket/dump_creds.py','--pass','uid=%s' % self.compte()])
else: else:
ret += coul(u' Il faudra penser à attribuer un mot de passe\n', 'jaune') ret += coul(u' Il faudra penser à attribuer un mot de passe\n', 'jaune')
# Le deuxième argument est le potentiel chemin de l'ancien compte # Le deuxième argument est le potentiel chemin de l'ancien compte
@ -2464,6 +2389,7 @@ class Adherent(BaseProprietaire):
finConn.append(facture._data['finConnexion'][0]) finConn.append(facture._data['finConnexion'][0])
self._set('debutConnexion', debutConn) self._set('debutConnexion', debutConn)
self._set('finConnexion', finConn) self._set('finConnexion', finConn)
self._save()
def adherentPayant(self, valeur = None): def adherentPayant(self, valeur = None):
""" """
@ -3191,7 +3117,7 @@ Contactez nounou si la MAC est bien celle d'une carte.""", 3)
self._set('prise', []) self._set('prise', [])
return return
if not re.match('^[a-cg-jkmopv][0-7][0-5][0-9]$', new.lower()): if not re.match('^[a-cg-jmopv][0-7][0-5][0-9]$', new.lower()):
raise ValueError('Prise incorrecte') raise ValueError('Prise incorrecte')
self._set('prise', [new.upper()]) self._set('prise', [new.upper()])
@ -3321,7 +3247,7 @@ Contactez nounou si la MAC est bien celle d'une carte.""", 3)
rid = int(self.rid()) rid = int(self.rid())
rid_obj = ridtools.Rid(rid) rid_obj = ridtools.Rid(rid)
if rid_obj.ipv4_dispo: if rid_obj.ipv4_dispo:
ip = unicode(rid_obj.ipv4()) ip = rid_obj.ipv4()
else: else:
ip = '' ip = ''
#On essaye d'attribuer un rid si ip auto et pas encore de rid #On essaye d'attribuer un rid si ip auto et pas encore de rid
@ -3350,7 +3276,7 @@ Contactez nounou si la MAC est bien celle d'une carte.""", 3)
# Si après tout ca, on a encore auto, c'est qu'il n'y a plus d'ip # Si après tout ca, on a encore auto, c'est qu'il n'y a plus d'ip
if ip == '<automatique>': if ip == '<automatique>':
raise RuntimeError(u"Plus d'IP (rid) libres dans %s." % mach_type) raise RuntimeError(u"Plus d'IP (rid) libres dans %s." % ' et '.join(net))
# Sinon, ip fournie, contrôle qu'elle est compatible avec le type machine # Sinon, ip fournie, contrôle qu'elle est compatible avec le type machine
# et calcule le rid qui va bien avec # et calcule le rid qui va bien avec
@ -4113,7 +4039,7 @@ class Facture(BaseClasseCrans):
proprio.solde(operation=art['nombre']*art["pu"], proprio.solde(operation=art['nombre']*art["pu"],
comment="Facture n°%s : %s" % comment="Facture n°%s : %s" %
(self.numero(), (self.numero(),
art['designation'].encode(config.ldap_encoding, errors='ignore'))) art['designation'].encode('utf-8', errors='ignore')))
proprio.save() proprio.save()
if self.modePaiement() == 'solde': if self.modePaiement() == 'solde':
proprio = self.proprietaire() proprio = self.proprietaire()
@ -4160,17 +4086,17 @@ class Facture(BaseClasseCrans):
self._set('article', self._set('article',
['%s~~%s~~%s~~%s' % ( ['%s~~%s~~%s~~%s' % (
art['code'], art['code'],
art['designation'], art['designation'].encode('utf-8', errors='replace'),
str(art['nombre']), str(art['nombre']),
str(art['pu'])) str(art['pu']))
for art in arts]) for art in arts])
# charge la liste des articles # charge la liste des articles
arts = [] arts = []
for art in self._data.get("article", []): for art in self._data.get("article", []):
art = art.split('~~') art = art.split('~~')
art = { 'code' : art[0], art = { 'code' : art[0],
'designation' : art[1].decode(config.ldap_encoding, errors='replace'), 'designation' : art[1].decode('utf-8', errors='replace'),
'nombre' : int(art[2]), 'nombre' : int(art[2]),
'pu' : float(art[3]) } 'pu' : float(art[3]) }
arts.append(art) arts.append(art)
@ -4204,7 +4130,6 @@ class Facture(BaseClasseCrans):
# ajoute les articles # ajoute les articles
if type(ajoute)==dict: if type(ajoute)==dict:
ajoute = [ajoute] ajoute = [ajoute]
if type(ajoute)==list: if type(ajoute)==list:
for art in ajoute: for art in ajoute:
if int(art['nombre']) != float(art['nombre']): if int(art['nombre']) != float(art['nombre']):
@ -4212,7 +4137,7 @@ class Facture(BaseClasseCrans):
if round(art['pu'], 2) != art['pu']: if round(art['pu'], 2) != art['pu']:
raise ValueError, u'pu ne doit pas avoir plus de 2 chiffres apres la virgule' raise ValueError, u'pu ne doit pas avoir plus de 2 chiffres apres la virgule'
art['nombre'] = int(art['nombre']) art['nombre'] = int(art['nombre'])
if '~~' in art['designation']: if '~~' in ' '.join([unicode(x) for x in art.values()]):
raise ValueError, u'Ne pas mettre de ~~ dans les champs' raise ValueError, u'Ne pas mettre de ~~ dans les champs'
arts.append(art) arts.append(art)
@ -4234,9 +4159,9 @@ class Facture(BaseClasseCrans):
_ = arts.pop() _ = arts.pop()
# on supprime les anciens articles # on supprime les anciens articles
if type(supprime) == dict: if type(supprime)==dict:
supprime = [supprime] supprime = [supprime]
if type(supprime) == list: if type(supprime)==list:
for art in supprime: for art in supprime:
arts.remove(art) arts.remove(art)
@ -4306,11 +4231,8 @@ class Facture(BaseClasseCrans):
def delete(self, comment=''): def delete(self, comment=''):
"""Suppression de la facture""" """Suppression de la facture"""
if self.controle(): if self.controle():
if max(self.proprietaire().connexion(), self.proprietaire().adhesion()) + cotisation.del_post_adh >= time.time(): raise EnvironmentError(u"La facture a déjà été controlée, contacter trésorerie")
raise EnvironmentError(u"La facture a déjà été controlée, contacter trésorerie")
self.__proprietaire = None self.__proprietaire = None
self._delete(self.dn, comment) self._delete(self.dn, comment)

View file

@ -1,31 +0,0 @@
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
import sys
from gestion.affich_tools import cprint
from gestion import mail
import time
import lc_ldap.shortcuts
import lc_ldap.crans_utils as crans_utils
# Attention, si à True envoie effectivement les mails
SEND=('--do-it' in sys.argv)
#ldap_filter=u"(&(finConnexion>=%(date)s)(aid=*)(!(chbre=????)))" % {'date': crans_utils.to_generalized_time_format(time.time())}
#ldap_filter=u"(|(uid=detraz))"
conn = lc_ldap.shortcuts.lc_ldap_readonly()
dest = conn.search(ldap_filter, sizelimit=2000)
From = 'respbats@crans.org'
print "%d destinataires (Ctrl + C pour annuler l'envoi)" % len(dest)
raw_input()
with mail.ServerConnection() as smtp:
for adh in dest:
print "Envoi du mail à %s" % adh.dn
if SEND:
smtp.send_template('all_coupure', {'adh': adh, 'From': From})
cprint(" Envoyé !")
else:
cprint(" (simulé)")

View file

@ -35,7 +35,7 @@ N'hésite pas à nous contacter pour toute question, remarque ou problème à
l'adresse cableurs@crans.org. l'adresse cableurs@crans.org.
Plus d'informations sont disponibles sur : Plus d'informations sont disponibles sur :
* https://intranet.crans.org/wifimap/ pour une carte de la couverture * https://intranet2.crans.org/wifimap/ pour une carte de la couverture
* https://wifi.crans.org/ pour configurer sa machine en WiFi * https://wifi.crans.org/ pour configurer sa machine en WiFi
En te souhaitant une bonne journée, En te souhaitant une bonne journée,

View file

@ -23,7 +23,7 @@ SEND=('--do-it' in sys.argv)
PREV=('--prev' in sys.argv) PREV=('--prev' in sys.argv)
ldap_filter=u"(&(finAdhesion>=%(date)s)(aid=*))" % {'date': crans_utils.to_generalized_time_format(time.time())} ldap_filter=u"(&(finAdhesion>=%(date)s)(aid=*))" % {'date': crans_utils.to_generalized_time_format(time.time())}
#ldap_filter=u"(|(uid=detraz)(uid=mespinasse)(uid=oudin)(uid=begel))" #ldap_filter=u"(|(uid=dstan)(uid=lasseri))"
conn=lc_ldap.shortcuts.lc_ldap_readonly() conn=lc_ldap.shortcuts.lc_ldap_readonly()
mailaddrs=set() mailaddrs=set()
@ -38,17 +38,17 @@ echecs=[]
From = 'ca@crans.org' From = 'ca@crans.org'
for To in mailaddrs: for To in mailaddrs:
cprint(u"Envoi du mail à %s" % To) cprint(u"Envoi du mail à %s" % To)
mailtxt = mail.generate('age', {'To':To, 'From': From, 'lang_info': 'English version below',}) mailtxt = mail.generate('age', {'To':To, 'From': From})
mailtxt["Reply-To"] = Header("ca@crans.org") mailtxt["Reply-To"] = Header("ca@crans.org")
# fichier = open(STATUTS_PATH, 'rb') fichier = open(STATUTS_PATH, 'rb')
# part = MIMEApplication(fichier.read(), 'pdf') part = MIMEApplication(fichier.read(), 'pdf')
# part.add_header('Content-Disposition', 'attachment', filename="statuts.pdf") part.add_header('Content-Disposition', 'attachment', filename="statuts.pdf")
# mailtxt.attach(part) mailtxt.attach(part)
# fichier = open(RI_PATH, 'rb') fichier = open(RI_PATH, 'rb')
# part = MIMEApplication(fichier.read(), 'pdf') part = MIMEApplication(fichier.read(), 'pdf')
# part.add_header('Content-Disposition', 'attachment', filename="reglement.pdf") part.add_header('Content-Disposition', 'attachment', filename="reglement.pdf")
# mailtxt.attach(part) mailtxt.attach(part)
if PREV: if PREV:
print mailtxt.as_string() print mailtxt.as_string()

View file

@ -7,22 +7,14 @@ affluences en perm"""
import sys import sys
import pytz import pytz
import datetime import datetime
from dateutil.parser import parse as parse_gtf
import calendar import calendar
from lc_ldap.shortcuts import lc_ldap_readonly from lc_ldap.shortcuts import lc_ldap_readonly
from lc_ldap.variables import base_dn from lc_ldap.variables import base_dn
from lc_ldap import crans_utils
import ldap import ldap
from affich_tools import coul from affich_tools import coul
import gestion.mail as mail_module import gestion.mail as mail_module
import gestion.config as config
PERIODE_TRANSITOIRE = [
crans_utils.datetime_from_generalized_time_format(date)
for date in
[config.gtf_debut_periode_transitoire, config.gtf_fin_periode_transitoire]
]
#: Une journée (c'est plus pratique) #: Une journée (c'est plus pratique)
DAY = datetime.timedelta(days=1) DAY = datetime.timedelta(days=1)
@ -31,7 +23,7 @@ DAY = datetime.timedelta(days=1)
FORMAT_LDAP = '%Y%m%d%H%M%S%z' FORMAT_LDAP = '%Y%m%d%H%M%S%z'
#: Infos à oublier dans un datetime pour ne garder que le jour #: Infos à oublier dans un datetime pour ne garder que le jour
ERASE_DAY = {'second': 0, 'minute': 0, 'microsecond': 0, 'hour': 0, } ERASE_DAY = { 'second': 0, 'minute': 0, 'microsecond': 0, 'hour': 0, }
#: filtre ldap max(finConnexion) \in intervalle #: filtre ldap max(finConnexion) \in intervalle
# NB: finConnexion est un attribut ldap multivalué, et on s'intéresse ici # NB: finConnexion est un attribut ldap multivalué, et on s'intéresse ici
@ -49,6 +41,7 @@ FILTRE_TPL_SIMPLE = u'(&(finConnexion>=%(debut)s)(!(finConnexion>=%(fin)s)))'
# min(a,b) >= v <=> a >= v /\ b >= v # min(a,b) >= v <=> a >= v /\ b >= v
# min(a,b) < v <=> a < v \/ b < v # min(a,b) < v <=> a < v \/ b < v
FILTRE_TPL = u"""(& FILTRE_TPL = u"""(&
(aid=*)
(&(finConnexion>=%(debut)s)(finAdhesion>=%(debut)s)) (&(finConnexion>=%(debut)s)(finAdhesion>=%(debut)s))
(|(!(finConnexion>=%(fin)s))(!(finAdhesion>=%(fin)s))) (|(!(finConnexion>=%(fin)s))(!(finAdhesion>=%(fin)s)))
)""" )"""
@ -66,38 +59,31 @@ FILTRE_TPL = u"""(&
def warn(mail_conn, adh): def warn(mail_conn, adh):
"""Envoie un mail d'avertissement à ``adh``, en utilisant la connexion mail """Envoie un mail d'avertissement à ``adh``, en utilisant la connexion mail
``mail_conn``""" ``mail_conn``"""
tpl_name = 'fin_connexion' fin = min(max(parse_gtf(v.value) for v in adh[l]) \
fields = ['finAdhesion'] for l in ['finConnexion', 'finAdhesion'] )
if 'aid' in adh:
fields.append('finConnexion')
else:
tpl_name += '_club'
fin = compute_fin_connexion(adh)
delai = (fin - datetime.datetime.now(pytz.UTC)).days delai = (fin - datetime.datetime.now(pytz.UTC)).days
data = { data = {
'delai': delai, 'delai': delai,
'adh': adh, 'adh': adh,
} }
for l in fields: for l in ['adhesion', 'connexion']:
fin = max(v.value for v in adh[l]) fin = max(parse_gtf(v.value) for v in adh['fin' + l.capitalize()])
data[l] = fin data['fin_%s' % l] = fin
deco = min(data[l] for l in fields)
if deco >= PERIODE_TRANSITOIRE[0] and deco < PERIODE_TRANSITOIRE[1]: From = 'respbats@crans.org'
data['sursis'] = PERIODE_TRANSITOIRE[1] To = adh.get_mail()
if not To:
print "No valid mail for %r" % adh
return
data.update({'To': To, 'From': From})
mailtxt = mail_module.generate('fin_connexion', data)
mail_conn.sendmail(From, [To], mailtxt.as_string())
data.update({'From': 'respbats@crans.org'})
mail_conn.send_template(tpl_name, data)
def compute_fin_connexion(adh): def compute_fin_connexion(adh):
"""Renvoie le datetime de fin effective de connexion de l'``adh``""" """Renvoie le datetime de fin effective de connexion de l'``adh``"""
fields = ['finAdhesion'] return min( max(parse_gtf(v.value) for v in adh['fin' + l])
if 'aid' in adh: for l in ['Adhesion', 'Connexion'])
fields.append('finConnexion')
value = min(max(v.value for v in adh[l]) for l in fields)
if value >= PERIODE_TRANSITOIRE[0] and value < PERIODE_TRANSITOIRE[1]:
return PERIODE_TRANSITOIRE[1]
return value
def select(conn, begin, to, mode='r'): def select(conn, begin, to, mode='r'):
"""Récupère les adhérents dont la connexion expire entre les datetimes """Récupère les adhérents dont la connexion expire entre les datetimes
@ -107,19 +93,6 @@ def select(conn, begin, to, mode='r'):
begin = begin.replace(tzinfo=pytz.UTC) begin = begin.replace(tzinfo=pytz.UTC)
if not to.tzinfo: if not to.tzinfo:
to = to.replace(tzinfo=pytz.UTC) to = to.replace(tzinfo=pytz.UTC)
# Si la période considérée contient la liste des gens qui seront déco à la
# fin de la période transitoire (c'est-à-dire qui seront déco à
# PERIODE_TRANSITOIRE[1] )
if begin <= PERIODE_TRANSITOIRE[1] and to > PERIODE_TRANSITOIRE[1]:
# Alors il est nécessaire de les considérer, donc de selectionner
# à partir du début de la période transitoire, au moins
begin = min(PERIODE_TRANSITOIRE[0], begin)
# Si la période considérée se termine pendant la période transitoire
if to < PERIODE_TRANSITOIRE[1] and to >= PERIODE_TRANSITOIRE[0]:
# Alors, il ne faut considérer que les adhérents qui se font déco avant
# le début de la période transitoire
to = PERIODE_TRANSITOIRE[0]
data = { 'debut': begin.strftime(FORMAT_LDAP), data = { 'debut': begin.strftime(FORMAT_LDAP),
'fin': to.strftime(FORMAT_LDAP), 'fin': to.strftime(FORMAT_LDAP),
} }
@ -128,11 +101,7 @@ def select(conn, begin, to, mode='r'):
# NB: on ne prend que les adhérents, d'où SCOPE_ONELEVEL # NB: on ne prend que les adhérents, d'où SCOPE_ONELEVEL
res = conn.search(filtre, scope=ldap.SCOPE_ONELEVEL, dn=base_dn, mode=mode) res = conn.search(filtre, scope=ldap.SCOPE_ONELEVEL, dn=base_dn, mode=mode)
def keep(adh): return res
"""Ne conserve que les adhérents ayant encore des machines"""
return bool(adh.machines())
return filter(keep, res)
def brief(c, debut, fin): def brief(c, debut, fin):
"""Renvoie la liste des adhérents dont la connexion expire entre """Renvoie la liste des adhérents dont la connexion expire entre
@ -147,9 +116,9 @@ def brief(c, debut, fin):
if "--list" in sys.argv: if "--list" in sys.argv:
for adh in to_warn: for adh in to_warn:
valeurs = [max(v.value for v in adh[l]) \ valeurs = [max(parse_gtf(v.value) for v in adh[l]) \
for l in ['finConnexion', 'finAdhesion'] ] for l in ['finConnexion', 'finAdhesion'] ]
[f_con, f_adh] = [coul(str(v), 'rouge' if v >= debut and v < fin else 'vert') \ [f_con, f_adh] = [ coul(str(v), 'rouge' if v >= debut and v < fin else 'vert') \
for v in valeurs] for v in valeurs]
print "%r %s %s;%s" % (adh, adh.dn.split(',', 1)[0], f_con, f_adh) print "%r %s %s;%s" % (adh, adh.dn.split(',', 1)[0], f_con, f_adh)
return to_warn return to_warn
@ -162,16 +131,12 @@ def prev_calendar(c, date):
cal = calendar.Calendar() cal = calendar.Calendar()
first = datetime.datetime(day=1, month=month, year=year, tzinfo=pytz.UTC) first = datetime.datetime(day=1, month=month, year=year, tzinfo=pytz.UTC)
last = first.replace(month=1+month%12, year=year+int(month == 12)) last = first.replace(month=1+month%12, year=year+int(month==12))
disconnect = select(c, first, last) disconnect = select(c, first, last)
by_day = {x: 0 for x in xrange(1, 32)} by_day = {x: 0 for x in xrange(1, 32)}
for adh in disconnect: for adh in disconnect:
date = compute_fin_connexion(adh) date = compute_fin_connexion(adh)
# On veut le .day, mais dans le fuseau UTC (utilisé pour le select juste
# avant). Avec UTC, c'est facile: il suffit de virer l'offset de la TZ
if date.tzinfo:
date -= date.tzinfo.utcoffset(date)
by_day[date.day] += 1 by_day[date.day] += 1
yield ['L', 'M', 'Me', 'J', 'V', 'S', 'D'] yield ['L', 'M', 'Me', 'J', 'V', 'S', 'D']

View file

@ -168,8 +168,12 @@ def generate(mail, params, lang=default_language, lang_fallback=default_language
def validation_url(view_name, data='', debug=False): def validation_url(view_name, data='', debug=False):
"""Enregistre une nouvelle url pour le module "validation" de l'intranet.""" """Enregistre une nouvelle url pour le module "validation" de l'intranet."""
import requests import requests
CA = '/etc/ssl/certs/cacert.org.pem' if debug:
ROOT = os.getenv('DBG_INTRANET', 'https://intranet.crans.org') CA = False
ROOT = 'https://intranet-dev.crans.org'
else:
CA = '/etc/ssl/certs/cacert.org.pem'
ROOT = 'https://intranet2.crans.org'
url = ROOT + '/validation/register/%s/' % view_name url = ROOT + '/validation/register/%s/' % view_name
payload = { payload = {
'data': json.dumps(data), 'data': json.dumps(data),
@ -250,7 +254,7 @@ class ServerConnection(object):
adh = data.get('adh', data.get('proprio', '')) adh = data.get('adh', data.get('proprio', ''))
to = data.get('to', None) or (adh.get_mail() if adh else None) to = data.get('to', None) or (adh.get_mail() if adh else None)
if to is None: if to is None:
print "Pas de mail valide pour %r. Skipping..." % (adh, ) print "No valid recipient mail. Aborting."
return return
# TODO: get lang toussa # TODO: get lang toussa
body = generate(tpl_name, data).as_string() body = generate(tpl_name, data).as_string()
@ -259,11 +263,3 @@ class ServerConnection(object):
def __exit__(self, type, value, traceback): def __exit__(self, type, value, traceback):
self._conn.quit() self._conn.quit()
# TODO: intégrer ceci dans le ServerConnection
def postconf(i):
"Fixe la fréquence d'envoi maximale par client (en msg/min)"
os.system("/usr/sbin/postconf -e smtpd_client_message_rate_limit=%s" % i)
os.system("/etc/init.d/postfix reload")
# opt = commands.getoutput("/usr/sbin/postconf smtpd_client_message_rate_limit")

View file

@ -5,10 +5,7 @@ adhérent. Le script se connecte à l'interface d'impression pour récupérer
la liste des dernières tâches imprimées. la liste des dernières tâches imprimées.
Ce script est lancé par un cron toutes les dix minutes sur zamok. Pour éviter Ce script est lancé par un cron toutes les dix minutes sur zamok. Pour éviter
de notifier plusieurs fois de la même fin d'impression, on ne balaie dans de notifier plusieurs fois de la même fin d'impression, on ne balaie dans
la liste que le dernier intervalle (bornes entières) de dix minutes la liste que le dernier intervalle (bornes entières) de dix minutes"""
detraz@crans.org
"""
import BeautifulSoup import BeautifulSoup
import sys import sys
@ -16,7 +13,9 @@ import requests
import re import re
import datetime import datetime
import time import time
import smtplib
from gestion.affich_tools import cprint
from gestion import mail from gestion import mail
from utils.sendmail import actually_sendmail from utils.sendmail import actually_sendmail
from lc_ldap import shortcuts from lc_ldap import shortcuts
@ -61,6 +60,7 @@ fin = now.replace(second=0, minute=(now.minute/10)*10, microsecond=0)
debut = fin - datetime.timedelta(minutes=10) debut = fin - datetime.timedelta(minutes=10)
success=dict() success=dict()
clubs=dict()
echecs=dict() echecs=dict()
for job in jobs: for job in jobs:
# Fin de parsing # Fin de parsing
@ -101,10 +101,19 @@ for job in jobs:
nb = split[0] nb = split[0]
name = split[1] name = split[1]
task = u':'.join(split[2:]) task = u':'.join(split[2:])
if u'@' in name:
#Seuls les clubs ont un @ dans leur alias, donc boucle
# dédiée au clubs
[name, club] = name.split(u'@', 1)
if club not in clubs:
clubs[club] = {'task': []}
clubs[club]['task'].append(task)
if name not in success: if name not in success:
success[name] = {'task': []} success[name] = {'task': []}
success[name]['task'].append(task) success[name]['task'].append(task)
#Section consacrée à l'envoi : partie 1 pour les adh, partie 2 pour les clubs
#To = 'detraz@crans.org'
From = 'impression@crans.org' From = 'impression@crans.org'
e = 0 e = 0
a = 0 a = 0
@ -115,11 +124,7 @@ for name in success:
a = a + 1 a = a + 1
adh = ad[0] adh = ad[0]
To = name + u'@crans.org' To = name + u'@crans.org'
try: tname = unicode(adh['prenom'][0]) + " " + unicode(adh['nom'][0])
tname = unicode(adh['prenom'][0]) + " " + unicode(adh['nom'][0])
# Pour les clubs
except KeyError:
tname = unicode(name)
codes = [x[0] + u'#' for x in digicode.list_code(name)] codes = [x[0] + u'#' for x in digicode.list_code(name)]
if not codes: if not codes:
codes = [digicode.gen_code(name) + u'#'] codes = [digicode.gen_code(name) + u'#']
@ -132,21 +137,36 @@ for name in success:
'taches': u', '.join(success[name]['task']), 'taches': u', '.join(success[name]['task']),
'codes': u', '.join(codes) 'codes': u', '.join(codes)
}) })
if VERB: #print mailtxt.as_string()
print mailtxt.as_string()
actually_sendmail(From, (To,), mailtxt) actually_sendmail(From, (To,), mailtxt)
else: else:
e = e+1 e = e+1
for club in clubs:
a = a + 1
tname = club
To = club + u'@crans.org'
codes = [x[0] + u'#' for x in digicode.list_code(club)]
if not codes:
codes = [digicode.gen_code(club) + u'#']
if VERB:
print (u"Envoi du mail à %s" % To)
mailtxt=mail.generate('mail_impression_ok', {
'To': To,
'From': From,
'tname': tname,
'taches': u', '.join(clubs[club]['task']),
'codes': u', '.join(codes),
})
#print mailtxt.as_string()
actually_sendmail(From, (To,), mailtxt)
for name in echecs: for name in echecs:
ad = con.search(u'(uid=%s)' % name) ad = con.search(u'(uid=%s)' % name)
To = 'impression@lists.crans.org' To = 'impression@lists.crans.org'
if ad <> []: if ad <> []:
adh = ad[0] adh = ad[0]
try: tname = unicode(adh['prenom'][0]) + " " + unicode(adh['nom'][0])
tname = unicode(adh['prenom'][0]) + " " + unicode(adh['nom'][0])
except KeyError:
tname = unicode(name)
else: else:
tname = name tname = name
mailtxt=mail.generate('mail_impression_ratee', { mailtxt=mail.generate('mail_impression_ratee', {
@ -155,8 +175,7 @@ for name in echecs:
'tname': tname, 'tname': tname,
'taches': u', '.join(echecs[name]['task']), 'taches': u', '.join(echecs[name]['task']),
}) })
if VERB: #print mailtxt.as_string()
print mailtxt.as_string()
actually_sendmail(From, (To,), mailtxt) actually_sendmail(From, (To,), mailtxt)
@ -167,3 +186,4 @@ if e>0:
print a print a
print "Nombre de mails non envoyés faute de résultats LDAP :" print "Nombre de mails non envoyés faute de résultats LDAP :"
print e print e
#print len(success)

View file

@ -1 +1 @@
Crans : Rappel assemblée générale extraordinaire du 26 novembre 2015 [Crans] Modification des statuts et du règlement intérieur : convocation en assemblée générale extraordinaire.

View file

@ -1,14 +1,32 @@
Dear members, Dear members,
We remind you that an Extraordinary General Meeting of the Crans will occur on Thursday, In order to be able to follow ENS Cachan when it'll move to Saclay, Crans need
November 26th, 2015 at 7.15 pm in Amphithéâtre Tocqueville. to reform its statutes and rules of procedure.
Before the meeting, you have to validate the agreement with CROUS and ENS that allow us to provide Internet in your appartment. That's why we hope you'll be a lot to vote because we need that at least 5% of you vote to validate it. As there are elections of CA/CVE/CS for the ENS, we will be with them at the Pavillon des Jardins between 10h and 19h. We therefore rewrote these texts, as it was an opportunity to clarify the way
the association is supposed to work, inside and towards external entities. These
new texts explicitly contains a lot of guarantees about how it works, espcially
regarding privacy (which tends to become a big deal nowadays).
We will find the agenda of the meeting and then the report on the wiki. Yet, due to timing issues, we need to have an extraordinary assembly to vote
https://wiki.crans.org/ComptesRendusCrans/Jeudi26Novembre2015 these texts on Thursday, July 9th at 7.00 pm (local time). This would allow us
to begin some files regarding Saclay project.
We hope to see you at this general meeting. However, we are aware that a lot of our members won't be able to come to vote.
Furthermore, these new texts haven't been translated into English for now.
Anyway, as it is a duty, these new versions are attached to the current email.
If you're interested in, you may contact us in order to ask some questions about
these new texts or the assembly. You just need to answer that email.
Previous versions of statutes and rules of procedure may be found there:
https://wiki.crans.org/CransAdministratif/StatutsDuCrans
https://wiki.crans.org/CransAdministratif/R%C3%A8glementInt%C3%A9rieur
The votes will occur all the day in Hall Villon, starting at 9.00 am (local
time). The assembly will be held in Amphithéâtre Tocqueville, and its goal will
be to account the votes of the day.
Sincerely,
-- --
Crans active members Crans active members

View file

@ -1,13 +1,37 @@
Chers adhérents, Chers adhérents,
Pour rappel, ce Jeudi 26 novembre 2015, à partir de 19h15, aura lieu une Assemblée Générale Extraordinaire du Crans dans l'Amphithéâtre Tocqueville. Dans le but de pouvoir nous investir dans le projet Paris-Saclay et ainsi
accompagner le déménagement de l'Ecole Normale Supérieure de Cachan,
l'association doit adapter ses statuts et son règlement intérieur.
Cette Assemblée sera précédée d'un vote très important : vous devrez valider la convention signée avec l'ENS et le CROUS qui nous permet de vous fournir Internet dans les résidences. Nous espérons donc que vous serez nombreux à venir, le vote devant mobiliser au moins 5% des adhérents pour être validé. Nous serons au Pavillon des Jardins avec les élections CA/CVE/CS de l'ENS de 10h à 19h. Nous avons donc travaillé à leur réécriture, qui vise aussi à clarifier la façon
dont l'association fonctionne, aussi bien en interne que vers les personnes
extérieures à l'association. Ainsi un certain nombre de garanties sont désormais
exprimées explicitement dans les statuts ou le règlement intérieur, en
particulier sur la notion de vie privée (qui est en ce moment au cœur des débats
publics).
Vous trouverez l'ordre du jour puis le compte-rendu de la réunion sur le wiki. Cependant, le temps manque, et nous aimerions pouvoir également mener les
https://wiki.crans.org/ComptesRendusCrans/Jeudi26Novembre2015 démarches nécessaires dans le cadre du projet Saclay, y compris auprès des
établissements publics concernés. Aussi, nous souhaiterions voter ces
modifications lors d'une Assemblée Générale Extraordinaire qui se tiendrait le
jeudi 9 juillet 2015 à 19h.
En espérant vous voir jeudi. Nous avons conscience que ce choix signifie aussi qu'un certain nombre d'entre
vous ne pourront pas nécessairement être présents. Les textes que nous
souhaitons adopter sont en pièce jointe du présent email. Si vous voyez des
choses qui vous semblent problématiques dans l'un d'entre eux, n'hésitez pas à
nous contacter par email (en répondant à celui-ci) pour exprimer votre pensée.
Les précédents statuts et règlement intérieur peuvent être trouvés ici :
https://wiki.crans.org/CransAdministratif/StatutsDuCrans
https://wiki.crans.org/CransAdministratif/R%C3%A8glementInt%C3%A9rieur
Vous pourrez vous rendre au Hall Villon à partir de 9h (heure locale) pour voter
concernant l'adoption des nouveaux textes. L'assemblée se tiendra à
l'amphithéâtre Tocqueville dans le but de dépouiller les votes.
Bien cordialement,
-- --
Les membres actifs du Crans Les membres actifs du Crans

View file

@ -1 +1 @@
[Crans] Coupure électrique le 27 Novembre 2015 Crans: coupure de courant le 31/01/2015

View file

@ -1 +1 @@
"{{ adh|name }}" <{{adh.get_mail()}}> "{{ adh|name }}" <{{to}}>

View file

@ -1,10 +1,14 @@
Chers adhérents, Chère adhérente, cher adhérent,
Une maintenance électrique est planifiée par le Crous demain vendredi 27 Le Crous nous a informé que l'alimentation électrique sera interrompue le
novembre, à partir de 15h, et provoquera une coupure générale sur l'ensemble du campus. samedi 31 janvier entre 9h à 17h, en raison de travaux de maintenance sur le
réseau électrique. Par conséquent, l'ensemble des services du Crans sera
indisponible durant cette maintenance. Davantage d'informations seront
fournies sur la page crans-incidents, accessible à cette adresse :
https://wiki.crans.org/CransIncidents .
L'accès à internet et tous les services du Crans seront interrompus durant l'intervention. Veuillez nous excuser pour la gêne occasionnée.
Cordialement, Cordialement,
-- --
Les membres actifs de l'association Crans les membres actifs du Crans

View file

@ -1,6 +1,6 @@
Bonjour {{tname}}, Bonjour {{tname}},
La machine {{mach}} dont tu es propriétaire a été déconnectée du réseau. La machine {{mach}} dont tu es le propriétaire a été déconnectée du réseau.
La raison de cette déconnexion est technique : le comportement de la machine est anormal. La raison de cette déconnexion est technique : le comportement de la machine est anormal.
Celle-ci émet des annonces signalant aux autres machines qu'elle peut gérer leur connexion Celle-ci émet des annonces signalant aux autres machines qu'elle peut gérer leur connexion

View file

@ -3,18 +3,14 @@ Hi {{ adh|name }}!
A new member of Crans has declared to live in the room {{chambre}} you were A new member of Crans has declared to live in the room {{chambre}} you were
previously registered in. This means we do not have your residency information previously registered in. This means we do not have your residency information
anymore. anymore.
{% if chbre_url %}
You can do update these data by following this link:
{{ chbre_url }}
{% endif %}
If you wish to keep your Crans Internet access, you need to give us the number If you wish to keep your Crans Internet access, you need to give us the number
of your new room or your new *complete* address in case you moved out of of your new room or your new *complete* address in case you moved out of
the campus. the campus.
If you do not wish to keep your Crans Internet access, all your computers will If you do not wish to keep your Crans Internet access, a simple message from
be deleted from our database. This deletion will automatically take place in you is enough to delete all your computers from our database. This deletion
{{jours}} day{% if jours > 1 %}s{%endif%} if you do not answer. will automatically take place in {{jours}} day{% if jours > 1 %}s{%endif%} if you do not answer.
If you do have a Crans account, you will keep an unlimited acces to it as well If you do have a Crans account, you will keep an unlimited acces to it as well
as all the associated services such as your email firstname.lastname@crans.org. as all the associated services such as your email firstname.lastname@crans.org.

View file

@ -3,20 +3,15 @@ Bonjour {{ adh|name }},
Un adhérent du Crans a déclaré résider dans la chambre {{chambre}}, Un adhérent du Crans a déclaré résider dans la chambre {{chambre}},
que tu occupais précédemment. Cela signifie que nous ne disposons que tu occupais précédemment. Cela signifie que nous ne disposons
plus d'informations de résidence valides à ton sujet. plus d'informations de résidence valides à ton sujet.
{%- if chbre_url %}
Afin de mettre à jour ces informations, nous t'invitons à utiliser le lien
ci-dessous :
{{ chbre_url }}
{%- endif %}
Si tu souhaites conserver ton accès Internet via le Crans, il est nécessaire Si tu souhaites conserver ton accès Internet via le Crans, il est nécessaire
que tu nous indiques ta nouvelle chambre, ou ta nouvelle adresse *complète*, que tu nous indiques ta nouvelle chambre, ou ta nouvelle adresse *complète*,
si tu as quitté le campus. si tu as quitté le campus.
Si tu ne souhaites pas conserver ton accès Internet, les machines que tu Si tu ne souhaites pas conserver ton accès Internet, un simple message de
possèdes seront supprimées de notre base de données. Cette suppression aura ta part et nous supprimerons les machines que tu possèdes de notre base de
automatiquement lieu dans {{jours}} jour{% if jours > 1%}s{%endif%} en l'absence données. Cette suppression aura automatiquement lieu dans {{jours}} jour{% if jours > 1%}s{%endif%} en
de réponse. l'absence de réponse.
Si tu possèdes un compte Crans, tu conserves un accès à celui-ci sans limite de Si tu possèdes un compte Crans, tu conserves un accès à celui-ci sans limite de
durée ainsi qu'à tous les services associés, notamment ton adresse mail durée ainsi qu'à tous les services associés, notamment ton adresse mail

View file

@ -1 +1 @@
"{{adh|name}}" <{{adh.get_mail()}}> "{{ adh|name }}" <{{To}}>

View file

@ -1,16 +1,13 @@
Cher adhérent du Crans, Cher adhérent du Crans,
Nous t'envoyons ce message pour t'informer que ta connexion arrive à Nous t'envoyons ce message pour t'informer que ta connexion arrive à
expiration dans moins de {{ delai }} jours.{% if finConnexion > finAdhesion %} expiration dans moins de {{ delai }} jours.{% if fin_connexion > fin_adhesion %}
En effet, ton adhésion annuelle s'achève le {{ finAdhesion|date}} même En effet, ton adhésion annuelle s'achève le {{ fin_adhesion|date}} même
si les frais de connexion restent acquis jusqu'au {{ finConnexion|date }}. si les frais de connexion restent acquis jusqu'au {{ fin_connexion|date }}.
{% else %} {% else %}
En effet, les frais de connexion sont valables jusqu'au {{ finConnexion|date }} En effet, les frais de connexion sont valables jusqu'au {{ fin_connexion|date }}
et ton adhésion annuelle s'achève le {{ finAdhesion|date }}. et ton adhésion annuelle s'achève le {{ fin_adhesion|date }}.
{%- endif %}{%- if sursis %} {% endif %}
Cependant, en raison de l'affluence de rentrée, ta connexion est prolongée
jusqu'au {{ sursis|date }}.{%- endif %}
Pour réadhérer ou prolonger ta connexion, tu peux nous retrouver Pour réadhérer ou prolonger ta connexion, tu peux nous retrouver
lors d'une de nos permanences. Les horaires actuels sont disponibles à lors d'une de nos permanences. Les horaires actuels sont disponibles à
l'adresse www.crans.org/PermanencesCrans . l'adresse www.crans.org/PermanencesCrans .

View file

@ -1 +0,0 @@
Les câbleurs du Crans <cableurs@crans.org>

View file

@ -1,2 +0,0 @@
Envoyé lorsque la connexion (effective, adh+connexion) d'un adhérent est sur le
point d'expirer.

View file

@ -1 +0,0 @@
[Crans] Fin de connexion dans moins de {{ delai }} jours

View file

@ -1 +0,0 @@
"{{adh|name}}" <{{ adh.get_mail() }}>

View file

@ -1 +0,0 @@
{{ mailer }}

View file

@ -1,20 +0,0 @@
Cher adhérent du Crans,
Nous t'envoyons ce message pour t'informer que la connexion du {{adh|name}},
dont tu es responsable, arrive à expiration dans moins de {{delai}} jours.
En effet, son adhésion annuelle s'achève le {{finAdhesion|date}}
{%- if sursis %}
Cependant, en raison de l'affluence de rentrée, la connexion est prolongée
jusqu'au {{ sursis|date }}.{%- endif %}
Pour réadhérer (gratuitement), tu peux nous retrouver lors d'une de nos
permanences. Les horaires actuels sont disponibles à
l'adresse www.crans.org/PermanencesCrans .
Le cas échéant, tu peux nous indiquer si tu souhaites modifier ou supprimer le
club, ses machines, ses imprimeurs ou le responsable.
À très bientôt !
--
Les membres actifs de l'association

View file

@ -1,7 +1,4 @@
Chers câbleurs,<br> Calendrier des déconnexions ce mois-ci:
Vous trouverez ci-dessus le résumé prévisionnel des fins de connexions
de nos adhérents pour ce mois-ci. Ce graphe peut ainsi vous donner une
idée des prochaines affluences en permanence.<br>
<table> <table>
{%- for line in calendar %} {%- for line in calendar %}
@ -23,6 +20,7 @@ idée des prochaines affluences en permanence.<br>
</tr> </tr>
{%- endfor %} {%- endfor %}
</table> </table>
<br>
-- <br> --
Les membres actifs de l'association Les membres actifs de l'association

Some files were not shown because too many files have changed in this diff Show more