Compare commits

...
Sign in to create a new pull request.

296 commits

Author SHA1 Message Date
Valentin Samir
1637a4aae4 Update radio urls bis 2015-12-06 19:51:25 +01:00
Valentin Samir
93d505745c Update url radios 2015-12-06 19:40:17 +01:00
Daniel STAN
7b26e38606 firewall4: ipset d'ouvertures ip/port temporaires 2015-12-03 13:46:41 +01:00
Gabriel Detraz
fe6f71acbc Le message de la date de fin d'adhesion est plus cohérent (plus 1er jan 1970) 2015-12-03 02:19:45 +01:00
Gabriel Detraz
cf76003c1f Pas de ticket si pas de compte crans 2015-12-03 00:36:29 +01:00
Gabriel Detraz
16777e3914 Ajout de l'impression de ticket mdp adhérent 2015-12-01 18:25:05 +01:00
Daniel STAN
a1256091b3 fin_connexion: précision sur le but 2015-12-01 15:01:36 +01:00
Gabriel Detraz
346861139c Impression de ticket machine avec gest_crans_lc 2015-12-01 12:03:23 +01:00
Pierre-Elliott Bécue
8f715ff771 Nettoyage et PEP8 pour chgpass 2015-12-01 02:44:32 +01:00
Pierre-Elliott Bécue
525c77c2ce Corrige la vérification des droits en cas de no_cracklib
ldap n'était pas défini à l'endroit du test
2015-12-01 02:31:33 +01:00
Pierre-Elliott Bécue
0918366bb8 Utilisation de l'encodage trouvé dans config (et drop de la variable locale) 2015-12-01 02:26:56 +01:00
Gabriel Detraz
fdca02be27 Les erreurs comportant des accents sont correctement gérées 2015-12-01 02:16:12 +01:00
Gabriel Detraz
229cd97fce Bug sournois, gest_crans plantait au premier echec du mdp crans 2015-12-01 01:51:37 +01:00
Gabriel Detraz
e7ba95b36d Permet de remettre la mac à auto, distinction entre ip auto et mac auto 2015-12-01 00:21:32 +01:00
Gabriel Detraz
3418dbb1b1 Revert "Code qui a introduit plus de bug qu'il n'en a résolu"
This reverts commit 9ea84a34bc.
2015-11-29 15:35:57 +01:00
Gabriel Detraz
450f6c49aa Ajoute les 2 pioneers 2015-11-29 03:26:20 +01:00
Gabriel Detraz
daedb2b657 Bug signalé par remi et mathilde concernant les blacklistes dans le futur, du à du mauvais cp/cl 2015-11-28 03:11:42 +01:00
Gabriel Detraz
8c00727456 Retire les bl mail invalide quand on modifie le champ mail ou mailext 2015-11-28 03:05:40 +01:00
Gabriel Detraz
e67b3be24b Vlan non utilisé 2015-11-28 00:21:30 +01:00
Daniel STAN
5e5a819b47 ldap_crans: kludge support pour >= et <= 2015-11-27 00:15:59 +01:00
Gabriel Detraz
4dd908e746 COupure du 27 novembre 2015-11-26 22:48:52 +01:00
Daniel STAN
69bff1c2da portail_captif: mention de la page de déco age 2015-11-26 19:35:31 +01:00
Pierre-Elliott Bécue
baf79bdda7 Nettoyage des restes de paiement par année scolaire
Ça fait plus d'un an qu'on est passés aux factures avec
 generalizedTimeFormat
2015-11-26 19:27:29 +01:00
Daniel STAN
af0e971764 freeradius: 'paiement' est une blacklist bloquante 2015-11-26 17:49:32 +01:00
Pierre-Elliott Bécue
14167847db Correction de la non prise en charge des blacklists appartements 2015-11-26 17:34:35 +01:00
Gabriel Detraz
8b86e04dba Nouvelles règles pour la limitation de debit et nouveau peering avec la dsi 2015-11-25 16:23:49 +01:00
Gabriel Detraz
cc0b6bf013 Rappel age 2015-11-24 23:29:41 +01:00
Gabriel Detraz
9ea84a34bc Code qui a introduit plus de bug qu'il n'en a résolu 2015-11-24 19:50:18 +01:00
Gabriel Detraz
cb0fda8c6c Crée une facture pour le rechargement arbitraire 2015-11-24 02:03:52 +01:00
Gabriel Detraz
023b354848 Déplace les clefs wifi 2015-11-24 01:07:53 +01:00
Gabriel Detraz
bb0bedf1fd Ménage d'hiver partie 2 2015-11-24 00:49:15 +01:00
Pierre-Elliott Bécue
b9bd5ab1fc affich_tools est déprécié, utilise affichage 2015-11-23 23:55:49 +01:00
Pierre-Elliott Bécue
46b5aca99f Nettoyage de affichage 2015-11-23 23:54:59 +01:00
Pierre-Elliott Bécue
bbcd49c88c Nettoyage d'hiver 2015-11-23 23:54:23 +01:00
Gabriel Detraz
4279b3db23 Paiement par note pour les ventes dans gest_crans_lc (comportement identique à gest_crans) 2015-11-22 20:15:19 +01:00
Gabriel Detraz
565a2ae8ed Menu pour les bornes v6, supporte l'absence d'ipv4 2015-11-19 15:14:40 +01:00
Pierre-Elliott Bécue
f52422622a Gestion du mode arbitraire inadéquate 2015-11-19 03:19:28 +01:00
Gabriel Detraz
e16ff77675 Fix machine appartements et gest_crans_lc 2015-11-19 02:30:14 +01:00
Pierre-Elliott Bécue
77ca50c48c Un peu de documentation 2015-11-19 02:22:47 +01:00
Gabriel Detraz
ad8846920e Modifications d'erreurs de label 2015-11-19 02:13:18 +01:00
Gabriel Detraz
09497c7c7a Pour les serveurs et bornes aussi, on vérifie que le suffixe va bien 2015-11-15 12:42:38 +01:00
Gabriel Detraz
20ddcb12bc On supprime toute référence à la carte étudiant 2015-11-15 11:48:09 +01:00
Gabriel Detraz
730db0107e Patch proposé par valentin : verifie que le login est bien un unicode 2015-11-15 11:36:12 +01:00
Cron Daemon
d989c9966c [ethercodes.dat] Mise à jour du fichier vendeur 2015-11-15 02:14:29 +01:00
Daniel STAN
8647650078 WhoIlo (ça peut dépanner) 2015-11-15 01:36:42 +01:00
Gabriel Detraz
dba410ceaf Retire du menu des cableurs les références aux ports de machines, aux blacklistes et au certif (droits nounou et bureau) 2015-11-14 22:33:04 +01:00
Gabriel Detraz
b36b5ba2ce Support de la creation de borne wifi 2015-11-14 18:31:26 +01:00
Gabriel Detraz
73617312b8 Véifie que la personne est ma pour créer une 2 eme machine filaire 2015-11-14 16:40:17 +01:00
Gabriel Detraz
1eed679e40 Regle le prb avec dialogrc et l'env 2015-11-14 14:18:06 +01:00
Gabriel Detraz
28cfce0916 Si il n'y a pas de suffixe, on le rajoute pour eviter les prb 2015-11-13 19:38:30 +01:00
Gabriel Detraz
f943cf52a6 Corrige un prb d'arg non désiré par la fonction menu (plantage de gest_crans_lc en création d'adhérent...) 2015-11-13 17:11:35 +01:00
Gabriel Detraz
9bfe935522 Feature pour gest_crans_lc (le solde est reconnu comme un article si c'est pas une modif arbitraire) 2015-11-13 16:27:33 +01:00
Gabriel Detraz
43cdcc9c6f Gest_crans_lc supporte la modification arbitraire de solde 2015-11-13 16:26:56 +01:00
Gabriel Detraz
e2141e1043 Adapte gest_crans_lc pour les datetime 2015-11-13 07:36:14 +01:00
Gabriel Detraz
19b13c39d7 Mail AGE 26/11/15 2015-11-12 12:34:28 +01:00
Daniel STAN
9194364ad0 check_repos: chdir avant de find 2015-11-10 21:05:59 +01:00
Daniel STAN
b88ba2a34f prise en compte du ~/.dialogrc 2015-11-10 20:38:47 +01:00
Daniel STAN
ef95c2b159 Revert "s/asterisk/idefisk/"
This reverts commit 99003ebaf3.
2015-11-10 20:38:00 +01:00
Daniel STAN
fc1b2c0edf deconnexion2: retire mention fiche deco (bis) 2015-11-09 02:41:15 +01:00
Vincent Le Gallic
310356eba9 [ressuscite_couteau_suisse] Afficher le vrai dn, c'est mieux 2015-11-04 21:51:44 +01:00
Pierre-Elliott Bécue
06d97c9aa3 dns accessible depuis config 2015-11-01 04:19:32 +01:00
Pierre-Elliott Bécue
51c0140dfb Ajout dans config.dns des domaines pour les mails crans 2015-11-01 04:15:11 +01:00
Valentin Samir
e4a36fb702 [gen_conf/bind] Pas d'enregistrement TLSA si le certificat est marqué comme révoqué 2015-10-30 17:07:08 +01:00
Gabriel Detraz
182597ca35 Suppression des anciens modules (avant crans_unifie) 2015-10-29 12:41:38 +01:00
Daniel STAN
f6609980fc mail: more info si skip envoi vers mail invalide 2015-10-26 11:46:25 +01:00
Pierre-Elliott Bécue
df4d564ffb host_exists ne marchait pas, le patch pour batb-5 avait coupé mac_prises 2015-10-25 22:06:41 +01:00
Valentin Samir
6f52c57431 Un petit script qui vérifie la correspondance entre les enregistrement TLSA du crans et les certificats présentés 2015-10-25 19:16:30 +01:00
Valentin Samir
f4e5ce3104 [bind] Seul le selecteur 0 est supporté pour l'enregistrement TLSA
On rend aussi accessible le condensa du certificat
2015-10-25 19:15:36 +01:00
Daniel STAN
d58b92b957 deconnexion2: ne caste plus get_mail() en unicode
S'il existe un mail valide, c'est déjà un unicode, sinon, c'est None, et il
ne faut pas le transformer en u"None".
2015-10-25 15:11:53 +01:00
Daniel STAN
22bf89eef0 old_wifi_machines: typo 2015-10-25 15:11:41 +01:00
Daniel STAN
b552af7739 Revert "Adherent.py sous lc_ldap"
This reverts commit b14847c625.
2015-10-22 23:17:30 +02:00
Daniel STAN
246cba6107 deconnexion: utilise send_template
Ce qui évite d'envoyer des mails à None.
2015-10-22 23:13:42 +02:00
Gabriel Detraz
b14847c625 Adherent.py sous lc_ldap 2015-10-21 23:35:27 +02:00
Gabriel Detraz
079831f5f3 Correction diverses 2015-10-21 16:29:45 +02:00
Gabriel Detraz
f975e60636 Moyens de paiement pour les factures 2015-10-21 15:41:12 +02:00
Gabriel Detraz
b2f1f9c038 Les modes de reglement pour le solde sont dans un fichier de conf 2015-10-21 01:28:08 +02:00
Gabriel Detraz
712b256943 Ne plante pas si les machines crans sont en mac auto 2015-10-18 22:29:45 +02:00
Pierre-Elliott Bécue
7a9dd65698 Optimisation pour le tri suivant les aid. Ajout de kikooness 2015-10-18 20:56:45 +02:00
Pierre-Elliott Bécue
fbe99ab65c Nettoyage, et ajout d'une option all pour récupérer toutes les factures 2015-10-18 20:33:22 +02:00
Pierre-Elliott Bécue
d49c87b6c3 Ajout d'une option pour limiter la période de recherche de factures 2015-10-18 20:12:10 +02:00
Pierre-Elliott Bécue
32e4416687 proprio()._id() -> proprio().oid() 2015-10-18 20:07:33 +02:00
Pierre-Elliott Bécue
2860fe931c Permet le tri par fid ou aid pour les factures. 2015-10-18 18:19:58 +02:00
Daniel STAN
6d4f6b0864 DBG_DJANGO_DB=localhost par défaut 2015-10-16 17:44:15 +02:00
Daniel STAN
44361f86ce testing.sh: rajout de DBG_CAS 2015-10-16 17:39:12 +02:00
Daniel STAN
196bc1db68 DBG_INTRANET est une adresse (donc https:// devant) 2015-10-16 17:37:19 +02:00
Pierre-Elliott Bécue
7d8f7c79ca "Non adhérent" en rouge. 2015-10-15 14:32:31 +02:00
Pierre-Elliott Bécue
f3186853ec Les timestamps dans l'historique contiennent des secondes 2015-10-15 14:32:01 +02:00
Cron Daemon
1969b7909b [ethercodes.dat] Mise à jour du fichier vendeur 2015-10-15 02:13:20 +02:00
Pierre-Elliott Bécue
99003ebaf3 s/asterisk/idefisk/ 2015-10-11 14:47:53 +02:00
Pierre-Elliott Bécue
e4c2a6e3bd Cleanup des dernières machines wifi non utilisées durant 31 jours 2015-10-11 14:47:14 +02:00
Pierre-Elliott Bécue
26a82dbb92 Rend SwitchNotFound plus verbeux 2015-10-11 14:46:21 +02:00
Pierre-Elliott Bécue
abaea5ee8d Typo 2015-10-11 14:45:58 +02:00
Pierre-Elliott Bécue
53bddc87b2 Fait en sorte que all_switchs ne forge pas les noms des switches 2015-10-11 14:42:25 +02:00
Pierre-Elliott Bécue
d7c15ab14b Typo à la con 2015-10-08 01:40:33 +02:00
Gabriel Detraz
a527a6f394 Ne plante pas sur le nom du subnet (reste de la réécriture de la fonction ip) 2015-10-06 00:46:12 +02:00
Daniel STAN
9f7c8ceaf3 paiement is now bl hard 2015-10-05 18:14:42 +02:00
Daniel STAN
af9d15bcc6 fin_connexion: plus besoin de dateutil.parse
Car generalizedTimeFormat.value est un dt localisé now (lc_ldap)
2015-10-05 00:28:20 +02:00
Pierre-Elliott Bécue
955dd379c7 Utilise les nouvelles variables de config et fonctions de crans_utils 2015-10-03 13:54:35 +02:00
Pierre-Elliott Bécue
6ae6e54101 Ajout des versions generalizedTimeFormat des configs de période transitoire 2015-10-03 13:46:43 +02:00
Pierre-Elliott Bécue
ba6baa40dc Les lignes TXT ont une longueur limite théorique de 255 caractères 2015-10-01 23:43:07 +02:00
Daniel STAN
50415d4a90 ipset.py: no more /usr/scripts/gestion 2015-10-01 22:05:37 +02:00
Pierre-Elliott Bécue
d4f67daea0 Changement de l'ordre d'affichage 2015-10-01 11:34:51 +02:00
Pierre-Elliott Bécue
a732024852 Ajout de références à l'aid/cid dans l'affichage 2015-10-01 00:09:26 +02:00
Daniel STAN
ade4a9d82e portail_captif: corrige path vers ipset 2015-09-30 23:29:21 +02:00
Daniel STAN
e7ba8c346d paiement passe en bl soft 2015-09-30 23:28:48 +02:00
Pierre-Elliott Bécue
22a05d3e1d Ajout de l'ESIGETEL à la liste des écoles 2015-09-30 14:04:04 +02:00
Pierre-Elliott Bécue
e2b8fd08f8 On écrit pas dans le .forward s'il existe déjà 2015-09-30 13:40:44 +02:00
Valentin Samir
588eba0515 [sip/asterisk] Ménage dans le code 2015-09-30 11:16:38 +02:00
Valentin Samir
ef0e8bb22e [sip/asterisk/Manager] Option pour ne pas attendre fullybooted
Par exemple si l'user n'a pas les droits de le recevoir
2015-09-30 10:48:22 +02:00
Charlie Jacomme
3dd845d333 Updating parsing according to status page 2015-09-29 19:41:53 +02:00
Charlie Jacomme
ea4d25f597 Deleting laserjet and changing cat name 2015-09-29 18:48:48 +02:00
Daniel STAN
5c6386abae oubli de python-netaddr 2015-09-29 10:42:34 +02:00
Valentin Samir
5b8ad633b3 [tv/radio] Mise a jour url BFM et sud radio 2015-09-28 13:42:37 +02:00
Daniel STAN
7aa2b6b688 testing.sh: ajoute DBG_DJANGO_DB 2015-09-28 13:28:11 +02:00
Pierre-Elliott Bécue
6a51e0c520 Patch pour batb-5.crans.org 2015-09-27 14:00:01 +02:00
Pierre-Elliott Bécue
ed91745fec Prettyprint du recuPaiement 2015-09-27 13:59:38 +02:00
Gabriel Detraz
a363b6927d Ignore batv-1 2015-09-26 22:26:53 +02:00
Pierre-Elliott Bécue
947047dea0 Pas de commentaire => commentaire vide, pas None 2015-09-26 16:28:11 +02:00
Pierre-Elliott Bécue
e46f6ba4d8 Typo 2015-09-26 02:49:16 +02:00
Pierre-Elliott Bécue
c700ec4c03 N'affiche pas la timezone du recuPaiement 2015-09-26 00:40:43 +02:00
Pierre-Elliott Bécue
c5c903b114 recuPaiement devient un generalizedTimeFormat 2015-09-26 00:36:03 +02:00
Gabriel Detraz
6da84bbfd1 Ajout des ugreen à 14 euros 2015-09-25 19:55:25 +02:00
Pierre-Elliott Bécue
ca77b3defa Prépare l'utilisation de generalized time format pour recuPaiement
* Et de datetimes timezone aware pour gérer le bousin
2015-09-25 01:26:01 +02:00
Pierre-Elliott Bécue
87382fbf6f Confusion and/or 2015-09-25 01:23:38 +02:00
Pierre-Elliott Bécue
4d7585574f db.search("nom=&prenom") == db.search("nom=*&prenom=*") == allAdherents 2015-09-25 01:22:11 +02:00
Pierre-Elliott Bécue
1df6eb37f5 step -= 1 si step = 0 ça fait tout drôle. 2015-09-25 01:18:08 +02:00
Pierre-Elliott Bécue
2c1674038e PEP8 + un peu de nettoyage 2015-09-25 01:17:46 +02:00
Pierre-Elliott Bécue
13555c735d set_solde passe un commentaire unicode à .solde dans ldap_crans 2015-09-25 00:31:10 +02:00
Gabriel Detraz
31f21003e7 Correction de bugs 2015-09-25 00:02:16 +02:00
Gabriel Detraz
bd4403d8ee Disclaimer si adhérent mineur 2015-09-24 23:35:55 +02:00
Gabriel Detraz
cb2a389077 Ajoute la note en mode de paiment pour les ventes 2015-09-24 13:59:03 +02:00
Gabriel Detraz
1a06bcf4f9 Corrige unicode encode error sur vente 2015-09-24 13:43:01 +02:00
Valentin Samir
86991c6f5f [wiki] Re Délogue si page pas publique et précédement pseudo-logué
cf commit a51d14f518 retiré par commit
6648b2e009
2015-09-24 12:44:04 +02:00
Gabriel Detraz
553b5f39ce Pas de genre dans le mail de deco pour RA 2015-09-23 23:25:58 +02:00
Pierre-Elliott Bécue
d8d31462f7 https://xkcd.com/859/ 2015-09-23 17:34:23 +02:00
Pierre-Elliott Bécue
5271e51f30 Un event qui traîne depuis trop longtemps en RAM est détruit. 2015-09-23 17:33:21 +02:00
Valentin Samir
de8832a3f9 [firewall4] typo s/set/self/ 2015-09-23 15:41:51 +02:00
Gabriel Detraz
87d971ac7e Nouveau modules unifié et lien vers le sites unifié 2015-09-22 20:27:33 +02:00
Pierre-Elliott Bécue
3abcb7b144 Script listant l'ensemble des membres actifs. 2015-09-22 17:55:07 +02:00
Valentin Samir
c2a21b8756 [firewall4] redirection ssh2.crans.org:80 --> zamok.crans.org:81
hts (le serveur du programme httptunnel) écoute sur zamok sur le port 81.
2015-09-22 16:41:23 +02:00
Daniel STAN
52a4cefdb5 radius:s/fil/adherents/ (config.rid_primaires only) 2015-09-21 22:21:46 +02:00
Pierre-Elliott Bécue
98b591978c Ajout de filtrage par mode de paiement, et correctif de bug 2015-09-20 18:57:01 +02:00
Pierre-Elliott Bécue
c653f97c03 Bout de code mort 2015-09-20 18:47:59 +02:00
Pierre-Elliott Bécue
4624363443 Script permettant le contrôle en masse de factures. 2015-09-20 16:19:14 +02:00
Pierre-Elliott Bécue
467565b90f controle_tresorier{,2,3} pointent vers des scripts disparus 2015-09-20 16:18:54 +02:00
Pierre-Elliott Bécue
5bb370bffb whos_lc peut désormais prêter sa fonction explore_db en RO. 2015-09-20 13:10:53 +02:00
Pierre-Elliott Bécue
1e769cc9a9 Passe la connexion en argument des fonctions en ayant besoin 2015-09-20 11:40:59 +02:00
Pierre-Elliott Bécue
0492338bf8 Sépare l'affichage de la recherche. 2015-09-20 11:24:19 +02:00
Pierre-Elliott Bécue
9e934c2e8c Parfois, des gens exemptent des machines sans IP ou MAC... 2015-09-20 05:05:47 +02:00
Gabriel Detraz
9507536d3e Bug pour les swicths publiques lors de la génération du schéma de brassage 2015-09-19 21:36:31 +02:00
Pierre-Elliott Bécue
fbf76bec6b Corrections mineures 2015-09-19 16:02:49 +02:00
Pierre-Elliott Bécue
59186fe37e Ajoute owncloud et mediadrop aux bases backupées 2015-09-19 11:40:21 +02:00
Pierre-Elliott Bécue
43d6795b53 On backupe horde5, pas horde 2015-09-19 11:34:02 +02:00
Gabriel Detraz
1d7b04a264 Radius dans la classe firewall wifi 2015-09-19 03:16:57 +02:00
Gabriel Detraz
4b165cef91 Ouli : si pas de radius, on envoie adh en non tagué sur tous les ports 2015-09-17 00:37:40 +02:00
Gabriel Detraz
16bc0eab01 Ajout de l'uplink de batb-5 2015-09-16 23:33:56 +02:00
Gabriel Detraz
b53a92b2a7 Les prises de borne du batiment k existent 2015-09-16 23:33:17 +02:00
Gabriel Detraz
4e10e930cd Support des switchs non adm par switch2.py (desactivation radius, mise en place de la bonne ip) 2015-09-16 17:10:57 +02:00
Cron Daemon
e7aa590314 [ethercodes.dat] Mise à jour du fichier vendeur 2015-09-16 17:10:57 +02:00
Daniel STAN
c9b3198e26 vieux trucs 2015-09-13 17:59:50 +02:00
Gabriel Detraz
587b76d930 Aiguillage entre site nas, filaire et wifi effectué dans le auth.py à présent (règle le prb de segfault sous jessie) 2015-09-13 13:59:33 +02:00
Daniel STAN
4915d64e90 auth.py: convertit data en dict une seule fois 2015-09-13 13:47:11 +02:00
Gabriel Detraz
80fff2f826 Pea va dans la bonne classe de parefeu wifi 2015-09-13 11:51:45 +02:00
Pierre-Elliott Bécue
de52de5059 On continue le ménage. 2015-09-10 16:22:13 +02:00
Pierre-Elliott Bécue
94185a8f81 Si pas de .forward, le mailredirect est "", pas "Inconnu". 2015-09-10 15:19:01 +02:00
Pierre-Elliott Bécue
062114d4ea PEP8 + arrêter de jouer à la roulette russe avec sys.argv 2015-09-10 04:17:27 +02:00
Gabriel Detraz
1262b47fb3 Forward pour l'intranet (executé en tant que root par www-data) 2015-09-09 22:01:13 +02:00
Pierre-Elliott Bécue
96a8ceca8d Option --do-it pour vraiment faire le recrédit, sinon affiche un message 2015-09-09 13:43:54 +02:00
Pierre-Elliott Bécue
18d6e674f1 Plus de disclaimer pour le premier usage de "crans" 2015-09-09 11:18:54 +02:00
Pierre-Elliott Bécue
11b2586819 recredite pointe vers un truc qui n'existe plus 2015-09-09 11:15:40 +02:00
Pierre-Elliott Bécue
be8247667f On sort recredit du path des membres actifs 2015-09-09 11:14:50 +02:00
Pierre-Elliott Bécue
9c89e7af0a update_solde est obsolète 2015-09-09 10:42:22 +02:00
Raphaël-David Lasseri
ecbe29d3fb [ldap_crans.py] On passe le chgpass a automatique par défaut 2015-09-08 22:57:59 +02:00
Pierre-Elliott Bécue
fe9f69d19a Liste aussi les contrôles. 2015-09-08 02:13:43 +02:00
Pierre-Elliott Bécue
4ee1ea55d1 Bug signalé par Hamza : soucis d'encodage dans le commentaire de modif solde 2015-09-07 23:49:45 +02:00
Pierre-Elliott Bécue
1c675f251c Le fait de savoir si l'adh est à jour dans factures_brief est inutile 2015-09-07 23:49:33 +02:00
Pierre-Elliott Bécue
fe37e87532 Quand on crée le compte Crans on appelle set_mail_ext 2015-09-07 22:12:28 +02:00
Pierre-Elliott Bécue
f1bcfde617 Script pour supprimer des queues de civet 2015-09-07 21:01:35 +02:00
Pierre-Elliott Bécue
29cb2a38f1 os.environ['VARIABLE'] c'est pas safe si VARIABLE n'existe pas 2015-09-07 21:00:56 +02:00
Daniel STAN
f527a12064 whos_lc: passe les arguments comme il faut au .py 2015-09-07 15:45:37 +02:00
Daniel STAN
4874b2eebc creer_compte_wiki.py: mdp encodé aussi 2015-09-06 23:02:53 +02:00
Gabriel Detraz
e2eb01f2aa Gestion des clubs avec la nouvelle app d'impression 2015-09-05 23:02:04 +02:00
Gabriel Detraz
d57588e313 Renvoie une chaine de carractères pour l'envoie de mail 2015-09-05 19:51:04 +02:00
Valentin Samir
dd1178d7d3 [munin] Ajout de odlyd à munin_fw 2015-09-05 13:11:42 +02:00
Pierre-Elliott Bécue
13b7bfd936 Enlève des machines qui ne sont plus actives à la kfet 2015-09-04 14:04:50 +02:00
Pierre-Elliott Bécue
55aac0e164 Change la période d'inactivité minimale pour que le cleanup marche 2015-09-04 14:04:16 +02:00
Pierre-Elliott Bécue
85f9ea46bb Essai de précision de reject pour le /32 de google 2015-09-04 14:03:33 +02:00
Pierre-Elliott Bécue
7c2543d90e Correction de la doc et de l'affichage des menus modifier adh 2015-09-04 14:02:08 +02:00
Charlie Jacomme
7ed5d5e829 [secrets_new] In debug mode, on raise notfound 2015-09-04 11:40:40 +02:00
Gabriel Detraz
394597c186 Evite de polluer cron avec active toutes les minutes 2015-09-02 10:32:05 +02:00
Pierre-Elliott Bécue
c035723e54 Laisse la possibilité de connecter si adhésion finit.
* Mais explicite le caractère non-réadhérant de la chose.
2015-09-01 19:44:40 +02:00
Pierre-Elliott Bécue
6f21fde1ea Les alias sont réservés aux comptes Crans 2015-09-01 19:25:44 +02:00
Pierre-Elliott Bécue
628036e8da s/fournitire/fourniture/ 2015-09-01 19:25:18 +02:00
Pierre-Elliott Bécue
6913629672 Refuse d'ajouter des connexions qui finissent après l'adhésion 2015-09-01 19:24:43 +02:00
Gabriel Detraz
f77a73218d Passage à systemctl, le nom du vpn change avec jessie 2015-09-01 17:55:03 +02:00
Pierre-Elliott Bécue
72bde6ca25 N'est pas un kikimeter 2015-09-01 14:03:02 +02:00
Pierre-Elliott Bécue
b10d2d1e95 Masque le menu solde pour un adh sans compte 2015-09-01 01:01:50 +02:00
Pierre-Elliott Bécue
b949b42364 Prévient la suppression d'adhérent ou de factures dans certaines conditions 2015-08-30 14:43:29 +02:00
Pierre-Elliott Bécue
00d67f1fc3 adher.delete peut raise, donc on utilise un bloc try 2015-08-30 14:42:30 +02:00
Pierre-Elliott Bécue
5a7e367e9c « BONJOUR, J'AI CLIQUÉ SUR CONNEXION ET ÇA NE M'A PAS RÉADHÉRÉ » 2015-08-29 21:36:18 +02:00
Daniel STAN
037ecab023 fin_connexion: warn possesseurs de machines only
Les autres n'ont déjà plus de connexion.
2015-08-29 15:56:49 +02:00
Gabriel Detraz
82b8fea9da Bug dans mail invalide, en cas de chbr diff de invalide 2015-08-29 13:02:36 +02:00
Daniel STAN
2a5c273b2c ldap_crans/ip(): manipule toujours une ip texte 2015-08-28 15:42:16 +02:00
Gabriel Detraz
a3c3290a26 Ajout d'un total sur stats_cableur 2015-08-28 14:41:10 +02:00
Pierre-Elliott Bécue
241b760921 Substitue macro_filtre par macro-filtre 2015-08-28 03:39:11 +02:00
Pierre-Elliott Bécue
8c41ce5a5b Ajout d'une macro pour faire une recherche bourrine
* whos -w ruoska recherche à la sauvage ruoska contre plein d'attributs
 * Attention, ça wildcarde à la fin
2015-08-28 03:35:57 +02:00
Pierre-Elliott Bécue
3944441033 Prise en charge d'une macro et de filtres spéciaux.
* whos_lc -f permet de chercher sur les prises, connexion=ok et
   adhesion=ok
 * whos_lc -P permet de chercher le proprio d'une machine ou d'une
   facture.
 * Quelques améliorations dans l'affichage
2015-08-28 03:15:48 +02:00
Pierre-Elliott Bécue
395db97395 Prépare l'arrivée de macros dans whos_lc 2015-08-28 02:18:18 +02:00
Pierre-Elliott Bécue
6161a4bc32 N'est plus utile. 2015-08-28 02:03:31 +02:00
Gabriel Detraz
3f70d1b825 Controle tresorier 1 et 2 aux archives 2015-08-27 23:27:26 +02:00
Raphaël-David Lasseri
89c8f33f68 [ldap_crans.py] On rajoute la possibilité de générer un mdp automatique
Possibilité de génération et d'impression d'un mot de passe crans automatiquement
2015-08-27 21:10:50 +02:00
Raphaël-David Lasseri
aac1b74dbf [gest_crans.py] Impression à la volée des logins/mdp wifi
On rajoute la possibilité d'imprimer directement les mdp wifi et on prépare
le terrain pour les factures
2015-08-27 20:23:30 +02:00
Pierre-Elliott Bécue
20cb9cdad0 Proprifie quota et affiche home à la plase de home-adh 2015-08-27 20:00:27 +02:00
Gabriel Detraz
9b255bbb30 Script d'alerte sms en cas de ro sur zbee (cron puis rabitmq) 2015-08-27 15:59:25 +02:00
Pierre-Elliott Bécue
118fa4700e Rend les noms de partoches plus clairs pour l'adh 2015-08-27 13:46:17 +02:00
Pierre-Elliott Bécue
4b7a8be09b On met des valeurs par défaut pour les études pour l'intranet 2015-08-27 02:17:21 +02:00
Pierre-Elliott Bécue
914390a891 whos_lc masque par défaut adresse et téléphone, et prend en charge ipsec, sshfp 2015-08-26 17:23:50 +02:00
Gabriel Detraz
79182f2b42 Creation d'un service alertsms sur zamok via rabbitmq 2015-08-26 01:51:37 +02:00
Gabriel Detraz
0adfe858cc Ne plante pas si l'objet est un club et non un adhérent 2015-08-25 20:44:36 +02:00
Daniel STAN
4c20f02f73 impression_hp: pdfinfo ne plante pas si warnings 2015-08-25 11:11:05 +02:00
Gabriel Detraz
13d8829e0d Passage à lc_ldap et modernisation 2015-08-23 20:05:03 +02:00
Pierre-Elliott Bécue
8c503584f9 Ajoute l'encodage de la base LDAP 2015-08-23 14:28:27 +02:00
Gabriel Detraz
c3ba567ec8 Modernisation et passage à lc_ldap de mail_invalide 2015-08-23 12:00:22 +02:00
Gabriel Detraz
fcfdfbced9 Si la mac est une mac de machine crans, on chercher par prise 2015-08-23 02:46:52 +02:00
Pierre-Elliott Bécue
8db871f115 Gère quand il n'y a pas de chambre 2015-08-22 20:34:57 +02:00
Pierre-Elliott Bécue
12c09f10b6 Ajoute la licence sur les scripts de hptools2 2015-08-22 05:18:22 +02:00
Pierre-Elliott Bécue
cc4c7c68c8 locate_mac à jour 2015-08-22 05:15:34 +02:00
Pierre-Elliott Bécue
36a299caa8 On gère l'encodage via config 2015-08-22 05:12:20 +02:00
Pierre-Elliott Bécue
2bc93d071b Permet de spécifier une liste dans populate_all_switches
* Grâce à cela, populate_all_switches peut être restreint à une liste
 spécifique de switches pour éviter de forcément tout peupler.
2015-08-22 04:34:58 +02:00
Gabriel Detraz
a2a8538b61 Modernisation et reparation de controle charte MA pour le secretaire 2015-08-22 03:38:35 +02:00
Gabriel Detraz
8d250bdc27 Stats prises est un vieux script de l'epoque crous 2015-08-22 02:24:21 +02:00
Gabriel Detraz
6c7c4ecf74 Surveillance auth.log plus necessaire 2015-08-22 02:03:01 +02:00
Gabriel Detraz
0b12a81e93 Passage de chbre_on_off à hptools2 (pour ra2.py) 2015-08-22 01:19:49 +02:00
Gabriel Detraz
ae803ba2c3 Répare stats vlans, utilise lc_ldap et hptools2 2015-08-22 00:41:59 +02:00
Daniel STAN
93d6b2d175 testing: ajoute DBG_COMNPAY 2015-08-20 23:30:08 +02:00
Pierre-Elliott Bécue
8d8164b80d C'était drôle quand c'était une private joke. 2015-08-20 16:11:25 +02:00
Pierre-Elliott Bécue
adeead08fc Quand on a mis à jour le solde, la fonction termine. 2015-08-20 15:54:08 +02:00
Pierre-Elliott Bécue
0774fb688f Affiche la date dans la case reçu des factures 2015-08-20 13:42:37 +02:00
Gabriel Detraz
6258828e31 Fusion de whosthere et whokfet, archivage de whokfet old 2015-08-18 16:38:52 +02:00
Gabriel Detraz
c0a2697aea Support des fonctions de whokfet (--all notamment pour les nounous) 2015-08-18 16:27:34 +02:00
Pierre-Elliott Bécue
9cb259ea71 Ajout de freebox.crans.org au 2B 2015-08-18 13:47:33 +02:00
Gabriel Detraz
2d76cea2f9 Move mid aux archives, les mid ne sont plus déplacés et restent fixes à présent 2015-08-18 12:36:00 +02:00
Gabriel Detraz
c34ebc5177 Passage de menage cableur à lc_ldap 2015-08-18 12:28:20 +02:00
Pierre-Elliott Bécue
94ee83b86a Nettoie list_firewall.py, plus un oublie dans list_exempt
* output est une liste d'unicodes, mieux vaut .join() sur un unicode
2015-08-18 04:15:43 +02:00
Pierre-Elliott Bécue
a5ddd73ce5 Nettoie list_exempt.py 2015-08-18 03:51:05 +02:00
Pierre-Elliott Bécue
21176bfcf9 Nettoie list_droits.py
* Fonctions plus bloc main
 * On n'utilise pas .rights(), car elle surcharge la liste des droits
 * Améliore un peu le code
2015-08-18 03:49:21 +02:00
Gabriel Detraz
f66fa61993 Methode .rights() et non plus recherche par droits bruts 2015-08-18 02:08:17 +02:00
Gabriel Detraz
9485236f2f Lc_ldap pour chsh 2015-08-18 02:01:34 +02:00
Gabriel Detraz
7e072aca3c Déplace les scripts de stats vers utils et non plus tools 2015-08-18 01:28:17 +02:00
Gabriel Detraz
dd32a30814 Passage de stats_ip à lc_ldap 2015-08-18 01:25:54 +02:00
Gabriel Detraz
8f3f9c13da Implémentation de missing tpe sur whosthere et archivage de who2b 2015-08-18 00:42:50 +02:00
Gabriel Detraz
a9593fe783 remplace who2b par whosthere 2b 2015-08-17 19:55:21 +02:00
Gabriel Detraz
39409ec894 Support des fonctions de who2b dans whosthere avec comme arg 2b 2015-08-17 19:28:33 +02:00
Gabriel Detraz
686bc45ed5 Passage de whosthere à lc_ldap 2015-08-17 16:36:02 +02:00
Gabriel Detraz
96676dee69 L'ancien stats_cableurs va aux archives, on utilise celui de /utils à présent 2015-08-17 13:57:17 +02:00
Gabriel Detraz
fb5f0888de Script déprécié, on utilise celui de home/ca 2015-08-17 13:52:36 +02:00
Gabriel Detraz
62d9379f03 List_droits passe à lc_ldap 2015-08-17 13:50:55 +02:00
Gabriel Detraz
09a720b827 list_firewall passe à lc_ldap 2015-08-17 13:24:28 +02:00
Gabriel Detraz
5f0247ffb3 Passage à lc_ldap pour la liste des exemptés 2015-08-17 12:47:28 +02:00
Cron Daemon
f8e7ffb3ed [ethercodes.dat] Mise à jour du fichier vendeur 2015-08-15 02:13:57 +02:00
Daniel STAN
6d44f7e538 Autorise paiement par 'note' 2015-08-14 18:03:15 +02:00
Daniel STAN
b9cecfbc9e retrait plugin stats-ip_ (redondant) 2015-08-13 19:09:40 +02:00
Daniel STAN
c6e3bf4f44 munin: calcul propre stats-ip 2015-08-13 19:06:00 +02:00
Pierre-Elliott Bécue
5568fc8bc7 Affiche l'historique d'un objet facture 2015-08-10 20:11:58 +02:00
Pierre-Elliott Bécue
cce8a7ff37 Ajoute l'aid/cid dans les détails d'une facture 2015-08-10 20:09:10 +02:00
Daniel STAN
c36a513747 PagesPerso: comptes() renvoie une liste de str
Ça répare l'affichage des pages perso pour les clubs.
2015-08-10 00:43:32 +02:00
Daniel STAN
0602ee1c5c firewall: redirection portail-captif pour bl-hard 2015-08-09 19:24:56 +02:00
Daniel STAN
22511a52a7 s/intranet2.crans.org/intranet.crans.org/ 2015-08-09 13:22:08 +02:00
Charlie Jacomme
5f1a1b1397 Adding conditionnal for --crans, no more exception 2015-08-08 03:42:12 +02:00
Daniel STAN
eac34dfc55 munin: switchs en fqdn 2015-08-07 13:08:00 +02:00
Hamza Dely
0907ab98d3 [gestion] Adaptation de chambres_vides pour les déménagements par intranet 2015-08-07 13:05:50 +02:00
Jordan Delorme
1ae148671d [Wiki] On empêche les anonymes de modifier les préférences ou ajouter
des quicklinks
2015-08-06 12:11:53 +02:00
Jordan Delorme
6648b2e009 [Wiki] L'action denied n'est plus utile pour gérer les ACL 2015-08-06 12:06:59 +02:00
Jordan Delorme
8a08dc2cc1 [Wiki] Thème pour les invités, pour ne pas modifier l'original 2015-08-06 12:05:17 +02:00
Jordan Delorme
e087de4f57 [Wiki] Parsers des pages wiki calendrier pour créer les .ics 2015-08-06 12:02:55 +02:00
Daniel STAN
818ff14d56 verify-cn: garde la backward comp tout de même 2015-08-05 20:02:59 +02:00
Daniel STAN
4504a3925d openvpn: refresh verify-cn (upstream) 2015-08-05 19:25:48 +02:00
Daniel STAN
29fdd087c3 ipset: /usr/sbin -> /sbin dans jessie 2015-08-05 19:03:57 +02:00
Pierre-Elliott Bécue
f53e64a14d Archives des fiches de déconnexion 2015-08-05 01:50:42 +02:00
Pierre-Elliott Bécue
b72cc11447 Retire la génération de la fiche de déconnexion. 2015-08-05 01:45:14 +02:00
Daniel STAN
9d53c8e882 màj mail prévention coupure 2015-08-03 20:12:03 +02:00
Raphaël-David Lasseri
6b2f41214f [anonymous_user.py]: reglages connexion anonyme 2015-08-02 01:16:36 +02:00
Valentin Samir
d457162767 Limite de la longeur des mots de passe a celle accepté par pam
Cela demanderait plus d'inverstigation pour trouver pourquoi pam échoue pour
les mots de passe de plus de 64 caractères
2015-07-30 15:56:47 +02:00
Daniel STAN
0a3c9a0324 màj template demenagement (pour futur validation) 2015-07-24 02:17:19 +02:00
Daniel STAN
4c8980a377 màj mail age (rappel) 2015-07-24 02:14:50 +02:00
Daniel STAN
418aa7b79b fin_connexion: previent aussi les clubs 2015-07-24 02:13:36 +02:00
Daniel STAN
52c3795255 ajoute DBG_INTRANET 2015-07-24 01:14:40 +02:00
Daniel STAN
ebeab8a0a0 /usr/scripts sans / final 2015-07-24 01:07:32 +02:00
Gabriel Detraz
650f55e7e8 Récupération du jid après impression 2015-07-24 00:28:00 +02:00
Daniel STAN
beb09f265b squelette de README.md 2015-07-23 14:18:03 +02:00
Daniel STAN
7efb8a4db6 fin_connexion: prend en compte période transitoire 2015-07-23 13:37:33 +02:00
Daniel STAN
8539e8c38d config: donne toujours la prochaine période transitoire
On se fiche de celle d'avant, une fois qu'elle est passée, et nous allons
avoir besoin de la suivante pour savoir qui avertir de sa fin de connexion
future.
2015-07-23 13:31:00 +02:00
Daniel STAN
dfa75aee76 fin_connexion: le calendrier est calculé en UTC 2015-07-22 21:12:43 +02:00
Valentin Samir
fbc9a4ef5c Mise a jour des source de la radio 2015-07-20 12:37:20 +02:00
Gabriel Detraz
8a015e0d16 Adapte recredit à la nouvelle app impression 2015-07-19 14:59:25 +02:00
Pierre-Elliott Bécue
8d410af8a3 Retrait des blacklistes obsolètes qui font planter generate 2015-07-18 21:53:36 +02:00
217 changed files with 25578 additions and 22913 deletions

11
.gitignore vendored
View file

@ -17,17 +17,8 @@
# 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
gestion/clef-wifi*
archive/gestion/clef-wifi*
# Autres dépôts git
gestion/logreader/

31
README.md Normal file
View file

@ -0,0 +1,31 @@
## 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,7 +1,8 @@
#! /usr/bin/env python
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
# Copyright (C) Stéphane Glondu, Alexandre Bos, Michel Blockelet
# Remanié en 2015 par Gabriel Détraz
# Licence : GPLv2
u"""Ce script permet au secrétaire de repérer plus facilement les membres
@ -21,22 +22,23 @@ Les commandes sont :
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
from affich_tools import coul, tableau, prompt, cprint
from gestion.affich_tools import coul, tableau, prompt, cprint
from utils.sendmail import actually_sendmail
from gestion import mail
# Importation de la base de données
from ldap_crans import crans_ldap, ann_scol
db = crans_ldap()
from lc_ldap import shortcuts
# Lors des tests, on m'envoie tous les mails !
from socket import gethostname
debug = False
# Conn à la db
ldap = shortcuts.lc_ldap_admin()
if __name__ == '__main__':
if len(sys.argv) > 3 and sys.argv[-2] == '--debug':
debug = sys.argv[-1]
@ -56,42 +58,37 @@ def _controle_interactif_adherents(liste):
restant = len(liste)
if restant == 0:
return 0, 0
cprint(u'\nContrôle des membre actifs' , 'cyan')
cprint(u"Pour chaque entrée, il faut taper 'o' ou 'n' (défaut=n).")
cprint(u"Une autre réponse entraîne l'interruption du processus.")
cprint(u"Le format est [nb_restant] Nom, Prénom (aid).")
cprint(u"")
nb = 0
for a in liste:
valeur = a.charteMA()
valeur = a['charteMA']
if valeur:
suggestion = 'o'
else:
suggestion = 'n'
ok = prompt(u'[%3d] %s, %s (%s) ?'
% (restant, a.nom(), a.prenom(), a.id()), suggestion, '').lower()
% (restant, a['nom'][0], a['prenom'][0], a['aid'][0]), suggestion, '').lower()
restant -= 1
if ok == 'o':
nb += 1
if a.charteMA() == False :
modifiable = db.search('aid=%s' % a.id(), 'w')['adherent'][0]
if modifiable._modifiable:
modifiable.charteMA(True)
cprint(modifiable.save())
else:
cprint(u'Adhérent %s locké, réessayer plus tard' % modifiable.Nom(), 'rouge')
elif ok == 'n':
if a.charteMA() == True:
modifiable = db.search('aid=%s' % a.id(), 'w')['adherent'][0]
if modifiable._modifiable:
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')
if a['charteMA'] != True :
modifiable = ldap.search(u'aid=%s' % a['aid'][0], mode='rw')
try:
with modifiable[0] as adh:
adh['charteMA']=True
adh.history_gen()
adh.save()
cprint(u'Controle OK')
except:
cprint(u'Adhérent %s locké, réessayer plus tard' % a['nom'][0], 'rouge')
elif ok != 'n':
cprint(u'Arrêt du contrôle des membres actifs', 'rouge')
break
return nb, len(liste)-nb
@ -99,12 +96,12 @@ def _controle_interactif_adherents(liste):
def liste_charte_nok():
"""Retourne la liste des membres actifs qui n'ont pas signé la charte."""
liste_actifs = db.search('droits=*')['adherent']
liste_actifs = ldap.search(u'droits=*')
liste_nok = []
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
and not adh.charteMA()):
and not adh['charteMA']):
liste_nok.append(adh)
return liste_nok
@ -113,14 +110,14 @@ def controle_interactif():
Procédure interactive de contrôle des chartes de membres actifs.
"""
todo_list = liste_charte_nok()
# 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
todo_list.sort(lambda x, y: cmp((x.nom(), x.prenom()), (y.nom(), y.prenom())))
todo_list.sort(lambda x, y: cmp((x['nom'][0], x['prenom'][0]), (y['nom'][0], y['prenom'][0])))
# Zou !
ok, nok = _controle_interactif_adherents(todo_list)
cprint(u'\nRécapitulatif des nouveaux contrôles :', 'violet')
liste = [[u'membres actifs', str(ok), str(nok)]]
cprint(tableau(liste,
@ -132,20 +129,19 @@ def spammer():
todo_list = liste_charte_nok()
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"
for adh in todo_list:
to = adh.email()
to = adh['mail'][0]
print to
if not debug:
data = config.mails.txt_charte_MA % {'From' : u"ca@crans.org", 'To' : to}
connexion.sendmail("ca@crans.org",to,data.encode('utf-8'))
From = u"ca@crans.org"
data=mail.generate('missing_charte_MA', {
'To': unicode(to),
'From': From,
})
actually_sendmail(u'ca@crans.org', (unicode(to),), data)
def __usage(message=None):
""" Comment ça marche ? """
cprint(__doc__ % { 'prog': sys.argv[0] })
@ -163,7 +159,7 @@ if __name__ == '__main__' :
__usage(u'Mauvaise utilisation de liste')
print "Liste des membres actifs n'ayant pas signé la charte :"
for adh in liste_charte_nok():
print adh.Nom()
print unicode(adh['prenom'][0]) + u" " + unicode(adh['nom'][0])
elif sys.argv[1] == 'modif':
if len(sys.argv) != 2:
__usage(u'Mauvaise utilisation de modif')

View file

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

View file

@ -1,12 +1,11 @@
#! /usr/bin/env python
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
import sys
# Copyright (C) Stéphane Glondu, Alexandre Bos
# Copyright (C) Stéphane Glondu, Alexandre Bos, et autres
# 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
sont plus sur le campus, ie ceux qui ne sont plus à jour de cotisation.
Utilisation :
@ -15,22 +14,20 @@ Utilisation :
Les commandes sont :
* lister afficher la liste des câbleurs succeptibles d'être
radiés
* radier selectionner, parmis eux, les cableurs que l'on
* radier selectionner, parmis eux, les cableurs que l'on
souhaite radier
"""
import sys, os, re
sys.path.append('/usr/scripts/gestion')
import config
from email_tools import send_email, parse_mail_template
import gestion.config
# Fonctions d'affichage
from affich_tools import coul, tableau, prompt, cprint
from gestion.affich_tools import coul, tableau, prompt, cprint
# Importation de la base de données
from ldap_crans import crans_ldap, ann_scol
db = crans_ldap()
from lc_ldap import shortcuts
ldap = shortcuts.lc_ldap_admin()
def _controle_interactif_adherents(liste):
"""
@ -40,38 +37,40 @@ def _controle_interactif_adherents(liste):
restant = len(liste)
if restant == 0:
return 0, 0
cprint(u'\nRadiation des câbleurs fantômes' , 'cyan')
cprint(u"Pour chaque entrée, il faut taper 'o' ou 'n' (défaut=n).")
cprint(u"Une autre réponse entraîne l'interruption du processus.")
cprint(u"Le format est [nb_restant] Nom, Prénom (aid).")
cprint(u"")
nb = 0
for a in liste:
ok = prompt(u'[%3d] %s, %s (%s) ?'
% (restant, a.nom(), a.prenom(), a.id()), 'n', '').lower()
% (restant, a['nom'][0], a['prenom'][0], a['aid'][0]), 'n', '').lower()
restant -= 1
if ok == 'o':
modifiable = db.search('aid=%s' % a.id(), 'w')['adherent'][0]
if modifiable._modifiable:
modifiable.droits([])
cprint(modifiable.save())
else:
cprint(u'Adhérent %s locké, réessayer plus tard' % modifiable.Nom(), 'rouge')
modifiable = ldap.search(u'aid=%s' % a['aid'][0], mode='rw')[0]
try:
with modifiable as adh:
adh['droits'].remove(u'Cableur')
adh.history_gen()
adh.save()
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':
cprint(u'Arrêt du contrôle %s des membres actifs' % explicite, 'rouge')
break
def candidats():
todo_list1 = db.search('droits=*')['adherent']
todo_list1 = ldap.search(u'droits=cableur')
todo_list = []
for adh in todo_list1:
if adh.droitsGeles():
if not adh.paiement_ok():
todo_list.append(adh)
todo_list.sort(lambda x, y: cmp((x.nom(), x.prenom()), (y.nom(), y.prenom())))
return todo_list
def lister():
"""
Afficher les câbleurs fantômes potentiels.
@ -80,19 +79,19 @@ def lister():
print "Liste des câbleur dont la cotisation n'est pas à jour."
print
for adh in todo_list:
print adh.prenom() + " " + adh.nom()
print unicode(adh['prenom'][0]) + u" " + unicode(adh['nom'][0])
print
print "total : " + str(len(todo_list))
def controle_interactif():
"""
Procédure interactive de radiations des câbleurs fantômes.
"""
todo_list = candidats()
# Zou !
_controle_interactif_adherents(todo_list)
def __usage(message=None):
""" Comment ça marche ? """
@ -103,22 +102,22 @@ def __usage(message=None):
if __name__ == '__main__' :
if len(sys.argv) <= 1:
__usage()
elif sys.argv[1] == 'lister':
if len(sys.argv) != 2:
__usage(u'Mauvaise utilisation de lister')
lister()
elif sys.argv[1] == 'radier':
if len(sys.argv) != 2:
__usage(u'Mauvaise utilisation de radier')
controle_interactif()
else:
__usage(u'Commande inconnue : %s' % sys.argv[1])
sys.exit(0)

View file

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

View file

@ -1,4 +1,4 @@
#! /usr/bin/env python
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
# #############################################################
# ..
@ -38,7 +38,8 @@ import commands
import shutil
import syslog
import stat
sys.path.append('/usr/scripts/')
if '/usr/scripts' not in sys.path:
sys.path.append('/usr/scripts')
from cranslib.deprecated import module as deprecated_module
deprecated_module()
from cranslib.utils import QuoteForPOSIX as escapeForShell

View file

@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# #############################################################
# ..
@ -31,10 +30,11 @@ Calcule le coût des options d'impression.
__version__ = '9.11'
import sys, os.path
sys.path.append('/usr/scripts/gestion')
from config import impression as config_impression
from gestion.config import impression as config_impression
from commands import getstatusoutput
sys.path.append('/usr/scripts/')
if '/usr/scripts' not in sys.path:
sys.path.append('/usr/scripts')
from cranslib.utils import logs
from subprocess import Popen, PIPE
from base import FichierInvalide, SoldeInsuffisant, PrintError, SettingsError
@ -415,9 +415,7 @@ class impression:
def _get_adh(self, adh):
if type(adh) == str:
sys.path.append("/usr/scripts/gestion/")
#from ldap_crans_test import crans_ldap
from ldap_crans import CransLdap
from gestion.ldap_crans import CransLdap
adh = CransLdap().getProprio(adh, 'w')
return adh

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
""" Envoie un mail avec la liste des serveurs qui ne sont pas synchro avec bcfg2.
@ -43,7 +43,6 @@ if __name__ == "__main__":
debug = "--debug" in sys.argv
if "--mail" in sys.argv:
if hosts != "":
sys.path.append("/usr/scripts/")
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)
elif debug:

View file

@ -4,19 +4,13 @@ if [[ $1 = "" ]] || [[ $1 = $USER ]] ; then
/usr/bin/quota
else
/usr/bin/quota $*
fi | awk -F'(:| *)' '
fi | sed 's/home-adh/home/' | awk -F'(:| *)' '
BEGIN { fs = "" }
/Disk/ { print; print "utilisé\tquota\tlimite\t%\t(en Mo)" }
{
if (NF == 2) { fs = $2 }
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*unit, $3/1024*unit, $4/1024*unit, $2*100/$3, fs
fs = ""
}
}'

View file

@ -70,10 +70,13 @@ BL_REJECT = [u'bloq']
BL_ISOLEMENT = [u'virus', u'autodisc_virus', u'autodisc_p2p', u'ipv6_ra']
#: place sur accueil
BL_ACCUEIL = []
BL_ACCUEIL = [u'paiement']
# Ces blacklists ont des effets soft (portail captif port 80)
#BL_ACCUEIL = [u'carte_etudiant', u'chambre_invalide', u'paiement']
# À classer:
# [u'carte_etudiant', u'chambre_invalide', ]
# 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,
# pour lequelles il est donc idiot d'activer la protection antisquattage:
@ -101,11 +104,14 @@ def radius_event(fun):
tuples en entrée en un dictionnaire."""
def new_f(auth_data):
data = dict()
for (key, value) in auth_data or []:
# Beware: les valeurs scalaires sont entre guillemets
# Ex: Calling-Station-Id: "une_adresse_mac"
data[key] = value.replace('"', '')
if type(auth_data) == dict:
data = auth_data
else:
data = dict()
for (key, value) in auth_data or []:
# Beware: les valeurs scalaires sont entre guillemets
# Ex: Calling-Station-Id: "une_adresse_mac"
data[key] = value.replace('"', '')
try:
return fun(data)
except Exception as err:
@ -213,7 +219,7 @@ def get_prise_chbre(data):
def realm_of_machine(machine):
"""Renvoie le `realm` d'une machine. Don't ask"""
if isinstance(machine, lc_ldap.objets.machineFixe):
return 'fil'
return 'adherents'
elif isinstance(machine, lc_ldap.objets.machineWifi):
return 'wifi-adh'
else:
@ -271,6 +277,18 @@ def instantiate(*_):
if TEST_SERVER:
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
def authorize_wifi(data):
"""Section authorize pour le wifi
@ -355,7 +373,7 @@ def authorize_fil(data):
return (radiusd.RLM_MODULE_UPDATED,
(),
(
("Auth-Type", "crans_fil"),
("Auth-Type", "Accept"),
),
)
@ -428,6 +446,15 @@ def authorize_nas(data, ldap):
("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
def post_auth_wifi(data):
"""Appelé une fois que l'authentification est ok.

View file

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

View file

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

View file

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

View file

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

View file

@ -1,37 +0,0 @@
# 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

@ -1,35 +0,0 @@
# 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

@ -0,0 +1,38 @@
# 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

@ -1,37 +0,0 @@
# 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 {
NAS-Identifier = "%{Packet-Src-IP-Address:-%{Packet-Src-IPv6-Address}}"
}
crans_nas
crans_unifie
}
}

View file

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

View file

@ -21,7 +21,7 @@ server inner-tunnel {
authorize {
#preprocess
crans_wifi
crans_unifie
#
# The chap module will set 'Auth-Type := CHAP' if we are
# 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
# additional steps we can take.
post-auth {
crans_wifi
crans_unifie
# Note that we do NOT assign IP addresses here.
# If you try to assign IP addresses for EAP authentication types,

View file

@ -558,7 +558,26 @@ if __name__ == "__main__":
time.sleep(1)
prettyDoin("Les carottes sont cuites." , "Ok")
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"]]
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"
]
]
titres = ("Nom", "Prénom", "Âge", "Adresse")
longueurs = [25, 25, '*', '*']
print tableau(data, titres, longueurs).encode(guess_preferred_encoding())

View file

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

View file

@ -18,11 +18,10 @@ import re
import affichage
import lc_ldap.shortcuts
from lc_ldap.crans_utils import to_generalized_time_format as to_gtf
import mail as mail_module
from config import demenagement_delai as delai, \
debut_periode_transitoire, periode_transitoire
gtf_debut_periode_transitoire, periode_transitoire
ERASE_DAY = { 'second': 0, 'minute': 0, 'microsecond': 0, 'hour': 0, }
DAY = datetime.timedelta(days=1)
@ -54,7 +53,7 @@ def get_kickout(adh):
def warn_or_delete(smtp, clandestin, fail, done):
"""Avertit l'adhérent ou supprime ses machines si nécessaire"""
date, exchambre = get_kickout(clandestin)
# Date de suppression prévue
date_suppr = date + delai*DAY
@ -72,16 +71,28 @@ def warn_or_delete(smtp, clandestin, fail, done):
mail_addr = clandestin.get_mail()
if not clandestin.machines() or not mail_addr:
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 = {
"from" : RESP,
"chambre" : exchambre,
"jours" : (date_suppr - now).days+1,
"to" : mail_addr,
"adh": clandestin,
"chbre_url" : chbre_url,
"chbre_url_error" : chbre_url_error,
"lang_info": "English version below",
}
mail = mail_module.generate('demenagement', data)
smtp.sendmail(RESP, [mail_addr], mail.as_string())
smtp.send_template('demenagement', data)
def format_entry(m):
"""Renvoie une ligne de tableau, pour une machine"""
@ -101,7 +112,7 @@ if __name__ == '__main__':
conn = lc_ldap.shortcuts.lc_ldap_admin()
if periode_transitoire:
date = to_gtf(debut_periode_transitoire)
date = gtf_debut_periode_transitoire
else:
date = now.strftime(FORMAT_LDAP) + 'Z'

View file

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

View file

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

View file

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

View file

@ -9,34 +9,35 @@ import datetime
# Fichier généré à partir de bcfg2
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"
##### Gestion des câblages
# 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
# Ne modifier que les dates !
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
# On récupère l'année scolaire à tout besoin
__annee = time.localtime()[0]
debut_periode_transitoire = time.mktime(time.strptime("%s/08/16 00:00:00" % (ann_scol,), "%Y/%m/%d %H:%M:%S"))
fin_periode_transitoire = time.mktime(time.strptime("%s/09/30 23:59:59" % (ann_scol,), "%Y/%m/%d %H:%M:%S"))
# Prochaine période transitoire de l'année version generalizedTimeFormat
gtf_debut_periode_transitoire = "%s0816000000+0200" % (__annee,)
gtf_fin_periode_transitoire = "%s0930235959+0200" % (__annee,)
#Sursis pour les inscription après le 1/11 pour fournir la carte étudiant
sursis_carte=8*24*3600
# Version timestampées timezone-naïves
debut_periode_transitoire = time.mktime(time.strptime("%s/08/16 00:00:00" % (__annee,), "%Y/%m/%d %H:%M:%S"))
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
# Les droits ne sont pas retires mais il n'y a plus de sudo
@ -54,89 +55,201 @@ quota_hard = 10000000
fquota_soft = 0
fquota_hard = 0
# Shell
login_shell='/bin/zsh'
club_login_shell='/usr/bin/rssh'
login_shell = '/bin/zsh'
club_login_shell = '/usr/bin/rssh'
# Longueur maximale d'un login
maxlen_login=25
maxlen_login = 25
shells_possibles = [u'/bin/csh',
u'/bin/sh', # tout caca
u'/bin/dash', # un bash light
u'/usr/bin/rc',
u'/usr/bin/ksh', # symlink vers zsh
u'/bin/ksh', # symlink vers zsh
u'/usr/bin/tcsh', # TENEX C Shell (csh++)
u'/bin/tcsh', # TENEX C Shell (csh++)
u'/bin/bash', # the Bourne-Again SHell
u'/bin/zsh', # the Z shell
u'/usr/bin/zsh', # the Z shell
u'/usr/bin/screen',
u'/bin/rbash', # Bash restreint
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/bin/passwd', # idem
u'/usr/local/bin/disconnect_shell', # déconnexion crans
u'/usr/scripts/surveillance/disconnect_shell', # idem
u'/usr/sbin/nologin', # This account is currently not available.
u'/bin/false', # vraiement méchant
u'/usr/bin/es', # n'exsite plus
u'/usr/bin/esh', # n'existe plus
u'', # le shell vide pour pouvoir les punis
shells_possibles = [
u'/bin/csh',
u'/bin/sh', # tout caca
u'/bin/dash', # un bash light
u'/usr/bin/rc',
u'/usr/bin/ksh', # symlink vers zsh
u'/bin/ksh', # symlink vers zsh
u'/usr/bin/tcsh', # TENEX C Shell (csh++)
u'/bin/tcsh', # TENEX C Shell (csh++)
u'/bin/bash', # the Bourne-Again SHell
u'/bin/zsh', # the Z shell
u'/usr/bin/zsh', # the Z shell
u'/usr/bin/screen',
u'/bin/rbash', # Bash restreint
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/bin/passwd', # idem
u'/usr/local/bin/disconnect_shell', # déconnexion crans
u'/usr/scripts/surveillance/disconnect_shell', # idem
u'/usr/sbin/nologin', # This account is currently not available.
u'/bin/false', # vraiement méchant
u'/usr/bin/es', # n'exsite plus
u'/usr/bin/esh', # n'existe plus
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 = {
"zsh": {"path":"/bin/zsh", "desc":"Le Z SHell, shell par defaut sur zamok"},
"bash": {"path":"/bin/bash", "desc":"Le Boune-Again SHell, shell par defaut de la plupart des linux"},
"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"},
"zsh" : {
"path" : "/bin/zsh",
"desc" : "Le Z SHell, shell par defaut sur zamok"
},
"bash" : {
"path" : "/bin/bash",
"desc" : "Le Boune-Again SHell, shell par defaut de la plupart des linux"
},
"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 ?
droits_groupes = {'adm' : [u'Nounou'],
'respbats' : [u'Imprimeur', u'Cableur', u'Nounou'],
'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'],
}
droits_groupes = {
'adm' : [
u'Nounou',
],
'respbats' : [
u'Imprimeur',
u'Cableur',
u'Nounou',
],
'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
modePaiement = ['liquide', 'paypal', 'solde', 'cheque', 'carte', 'comnpay', 'arbitraire',]
modePaiement = [
'liquide',
'paypal',
'solde',
'cheque',
'carte',
'comnpay',
'arbitraire',
'note',
]
####### Les ML
# Le + devant un nom de ML indique une synchronisation
# ML <-> fonction partielle : il n'y a pas d'effacement automatique
# des abonnés si le droit est retiré
droits_mailing_listes = {'roots' : [ u'Nounou', u'Apprenti'],
'mailman' : [ u'Nounou'],
'+nounou' : [ u'Nounou', u'Apprenti'],
'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'],
droits_mailing_listes = {
'roots' : [
u'Nounou',
u'Apprenti',
],
'mailman' : [
u'Nounou',
],
'+nounou' : [
u'Nounou',
u'Apprenti',
],
'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',
],
'+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.
'+dsi-crans' : [u'Nounou', u'Bureau'],
'+crous-crans' : [u'Nounou', u'Bureau'],
'+wrc' : [u'Webradio'],
}
# Correspondance partielle nécessaire... Des adresses non-crans sont inscrites à ces ML.
'+dsi-crans' : [
u'Nounou',
u'Bureau',
],
'+crous-crans' : [
u'Nounou',
u'Bureau',
],
'+wrc' : [
u'Webradio',
],
}
#: Répertoire de stockage des objets détruits
cimetiere = '/home/cimetiere'
@ -160,21 +273,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
# 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 = {
"rsa" : (1, "ssh-rsa"),
"dsa" : (2, "ssh-dss"),
"ecdsa-256" : (3, "ecdsa-sha2-nistp256"),
"ecdsa-384" : (3, "ecdsa-sha2-nistp384"),
"ecdsa-521" : (3, "ecdsa-sha2-nistp521"),
"ecdsa" : (3, "ecdsa-sha2-nistp521"),
}
"rsa" : (1, "ssh-rsa"),
"dsa" : (2, "ssh-dss"),
"ecdsa-256" : (3, "ecdsa-sha2-nistp256"),
"ecdsa-384" : (3, "ecdsa-sha2-nistp384"),
"ecdsa-521" : (3, "ecdsa-sha2-nistp521"),
"ecdsa" : (3, "ecdsa-sha2-nistp521"),
}
sshfs_ralgo = {}
for key, value in sshfp_algo.items():
sshfs_ralgo[value[1]] = (value[0], key)
sshfp_hash = {
"sha1" : 1,
"sha256" : 2,
"sha1" : 1,
"sha256" : 2,
}
sshkey_max_age = int(9.869604401089358 * (365.25 * 24 * 3600))
@ -212,40 +325,74 @@ plage_ens = '138.231.0.0/16'
# clefs qui cassent la bijectivité, mais qui peuvent servir.
# NETs est l'union des deux
NETs_primaires = {
'serveurs' : ['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',
'138.231.148.32/27',
'138.231.148.64/26',
'138.231.148.128/25',
'138.231.149.0/24',
'138.231.150.0/23',
],
'bornes' : [
'138.231.148.0/27',
],
'adm' : ['10.231.136.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'],
}
'serveurs' : [
'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',
'138.231.148.32/27',
'138.231.148.64/26',
'138.231.148.128/25',
'138.231.149.0/24',
'138.231.150.0/23',
],
'bornes' : [
'138.231.148.0/27',
],
'adm' : [
'10.231.136.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 = {
'all' : ['138.231.136.0/21', '138.231.144.0/21'],
'wifi': ['138.231.144.0/21'],
'fil' : ['138.231.136.0/21'],
}
'all' : [
'138.231.136.0/21',
'138.231.144.0/21',
],
'wifi': [
'138.231.144.0/21',
],
'fil' : [
'138.231.136.0/21',
],
}
NETs = {}
NETs.update(NETs_primaires)
NETs.update(NETs_secondaires)
NETs_regexp = { 'all' : '^138\.231\.1(3[6789]|4[0123456789]|5[01])\.\d+$' }
NETs_regexp = {
'all' : r'^138\.231\.1(3[6789]|4[0123456789]|5[01])\.\d+$'
}
# Classes de rid
# Merci d'essayer de les faire correspondre avec les réseaux
@ -282,13 +429,13 @@ rid_primaires = {
'personnel-ens' : [(55296, 55551),],
# Un unique rid pour les machines multicast
'multicast' : [(65535, 65535),],
}
}
rid_secondaires = {
# Rid pour les machines filaire ipv4
'fil' : [(0, 2047),],
'wifi' : [(2048, 4095), (34816, 35071),],
}
}
rid = {}
rid.update(rid_primaires)
@ -314,24 +461,59 @@ ipv6_machines_speciales = {
}
# Les préfixes ipv6 publics
prefix = { 'subnet' : [ '2a01:240:fe3d::/48' ],
'serveurs' : [ '2a01:240:fe3d:4::/64' ],
'adherents' : [ '2a01:240:fe3d:4::/64' ],
'fil' : [ '2a01:240:fe3d:4::/64' ],
'adm' : [ '2a01:240:fe3d:c804::/64' ],
'adm-v6' : [ '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' ],
}
prefix = {
'subnet' : [
'2a01:240:fe3d::/48',
],
'serveurs' : [
'2a01:240:fe3d:4::/64',
],
'adherents' : [
'2a01:240:fe3d:4::/64',
],
'fil' : [
'2a01:240:fe3d:4::/64',
],
'adm' : [
'2a01:240:fe3d:c804::/64',
],
'adm-v6' : [
'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)
int_prefix = {
@ -340,10 +522,12 @@ int_prefix = {
}
# Domaines dans lesquels les machines sont placées suivant leur type
domains = { 'machineFixe': 'crans.org',
'machineCrans': 'crans.org',
'machineWifi': 'wifi.crans.org',
'borneWifi': 'wifi.crans.org' }
domains = {
'machineFixe': 'crans.org',
'machineCrans': 'crans.org',
'machineWifi': 'wifi.crans.org',
'borneWifi': 'wifi.crans.org',
}
# VLans
vlans = {
@ -363,8 +547,6 @@ vlans = {
'v6only': 6,
# Vlan isolement
'isolement' : 9,
# Vlan de tests de chiffrement DSI
'chiffrement': 11,
# VLan des appartements de l'ENS
'appts': 21,
# Vlan federez-wifi
@ -377,33 +559,45 @@ vlans = {
'iscsi': 42,
# freebox (pour faire descendre la connexion au 0B)
'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
authorized_icmpv6 = ['echo-request', 'echo-reply', 'destination-unreachable',
'packet-too-big', 'ttl-zero-during-transit', 'parameter-problem']
authorized_icmpv6 = [
'echo-request',
'echo-reply',
'destination-unreachable',
'packet-too-big',
'ttl-zero-during-transit',
'parameter-problem',
]
output_file = { 4 : '/tmp/ipt_rules',
6 : '/tmp/ip6t_rules'
}
output_file = {
4 : '/tmp/ipt_rules',
6 : '/tmp/ip6t_rules',
}
file_pickle = { 4 : '/tmp/ipt_pickle',
6 : '/tmp/ip6t_pickle'
}
file_pickle = {
4 : '/tmp/ipt_pickle',
6 : '/tmp/ip6t_pickle',
}
##################################################################################
#: Items de la blackliste
@ -427,7 +621,7 @@ blacklist_sanctions = [
]
#: Blacklistes redirigeant le port 80 en http vers le portail captif (avec des explications)
blacklist_sanctions_soft = [
blacklist_sanctions_soft = [
'ipv6_ra',
'mail_invalide',
'virus',
@ -441,10 +635,24 @@ blacklist_bridage_upload = ['autodisc_upload', 'upload']
##################################################################################
adm_users = [ 'root', 'identd', 'daemon', 'postfix', 'freerad', 'amavis',
'nut', 'respbats', 'list', 'sqlgrey', 'ntpd', 'lp' ]
adm_users = [
'root',
'identd',
'daemon',
'postfix',
'freerad',
'amavis',
'nut',
'respbats',
'list',
'sqlgrey',
'ntpd',
'lp',
]
open_ports = { 'tcp' : '22' }
open_ports = {
'tcp' : '22',
}
# Debit max sur le vlan de la connexion gratuite
debit_max_radin = 1000000
@ -454,13 +662,83 @@ debit_max_gratuit = 1000000
## Vlan accueil et isolement ##
###############################
accueil_route = {
'138.231.136.1':{'tcp':['80','443', '22'],'hosts':['intranet.crans.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']},
'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']},
'138.231.136.1' : {
'tcp' : [
'80',
'443',
'22'
],
'hosts' : [
'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']

View file

@ -10,11 +10,17 @@
# Délai minimal avant de pouvoir réadhérer.
# Ne tient pas compte de la période transitoire, qui est un confort
# pour l'administration.
delai_readh_jour = 28
delai_readh_jour = 32
delai_readh = delai_readh_jour * 86400
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 à
# 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

View file

@ -130,6 +130,14 @@ 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
menteur_clients = [
"138.231.136.210",

View file

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

View file

@ -12,10 +12,14 @@ ITEMS = {
'designation': u'Cable Ethernet 5m',
'pu': 3.,
},
'ADAPTATEUR': {
'designation': u'Adaptateur Ethernet/USB',
'ADAPTATEUR_TrendNet': {
'designation': u'Adaptateur 10/100 Ethernet/USB-2',
'pu': 17.,
},
'ADAPTATEUR_UGreen': {
'designation': u'Adaptateur 10/100/1000 Ethernet/USB-3',
'pu': 14.,
},
'RELIURE': {
'designation': u'Reliure plastique',
'pu': 0.12,
@ -37,3 +41,27 @@ ITEMS = {
'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,12 +53,20 @@ mask = [24]
now=datetime.datetime.now()
if now.hour >= 6 and now.hour < 19 and now.weekday() < 5 and not is_ferie():
#: Débit maximal autorisé
debit_max = 150 # mbits per second en connexion de jour
debit_max = { 'total' : 250,
'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 ?
debit_jour = True
else:
#: Débit maximal autorisé
debit_max = 500 # mbits per second en conn de nuit et du week-end
debit_max = { 'total' : 600,
'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 ?
debit_jour = False
@ -73,7 +81,7 @@ federez_upload_max = 10 #mbytes per second
# Debit appartement down max
# TODO : mettre en place dans komaz.py
appt_download_max = debit_max/10
appt_download_max = debit_max['total']/10
#: Liste des réseaux non routables
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 os
debug = (int(os.environ['DBG_TRIGGER']) == 1) or True
debug = (int(os.environ.get('DBG_TRIGGER', 0)) == 1) or True
log_level = "info"
# Serveur maître
@ -16,6 +16,12 @@ user = "trigger"
port = 5671
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
# 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

View file

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

View file

@ -9,6 +9,8 @@ Licence : GPLv3
import sys
import time
import datetime
import subprocess
import pytz
import dateutil.relativedelta
if '/usr/scripts' not in sys.path:
sys.path.append('/usr/scripts')
@ -18,6 +20,7 @@ import config.cotisation
import lc_ldap.objets as objets
import lc_ldap.attributs as attributs
from lc_ldap.attributs import UniquenessError
from lc_ldap import crans_utils
import proprio
from CPS import TailCall, tailcaller, Continue
@ -45,20 +48,20 @@ class Dialog(proprio.Dialog):
'GPGFingerprint' : [a.nounou, a.soi],
'Remarques' : [a.cableur, a.nounou],
'Droits':[a.nounou, a.bureau],
'Blackliste':[a.cableur, a.nounou],
'Blackliste':[a.bureau, a.nounou],
'Vente':[a.cableur, a.nounou],
'Supprimer':[a.nounou, a.bureau],
}
menu = {
'Administratif' : {'text' : "Adhésion, carte étudiant, chartes", "callback":self.adherent_administratif},
'Personnel' : {'text' : "Nom, prénom, téléphone... (ajouter l'age ?)", 'callback':self.adherent_personnel},
'Administratif' : {'text' : "Adhésion, chartes", "callback":self.adherent_administratif},
'Personnel' : {'text' : "Nom, prénom, téléphone, et mail de contact", 'callback':self.adherent_personnel},
'Études' : {'text' : "Étude en cours", "callback":self.adherent_etudes},
'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"},
'GPGFingerprint' : {'text':'Ajouter ou supprimer une empeinte GPG', 'attribut':attributs.gpgFingerprint},
'Remarques' : {'text':'Ajouter ou supprimer une remarque de la machine', 'attribut':attributs.info},
'Remarques' : {'text':'Ajouter ou supprimer une remarque à cet adhérent', 'attribut':attributs.info},
'Droits' : {'text':"Modifier les droits alloués à cet adhérent", "callback":self.adherent_droits},
'Blackliste' : {'text': 'Modifier les blacklist de la machine', 'callback':self.modif_adherent_blacklist},
'Blackliste' : {'text': 'Modifier les blacklist de cet adhérent', 'callback':self.modif_adherent_blacklist},
'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))},
}
@ -123,17 +126,13 @@ class Dialog(proprio.Dialog):
"Adhésion": [a.cableur, a.nounou],
'Connexion': [a.cableur, a.nounou],
"Charte MA" : [a.nounou, a.bureau],
"Carte Étudiant" : [a.nounou, a.cableur, a.tresorier],
}
menu = {
"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},
"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},
}
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")
def box(default_item=None):
return self.dialog.menu(
@ -221,8 +220,7 @@ class Dialog(proprio.Dialog):
# Boite si on ne peux pas réahdérer
def box_already(end):
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),
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),
width=0,
height=0,
timeout=self.timeout,
@ -230,9 +228,8 @@ class Dialog(proprio.Dialog):
# Boite de confirmation à l'ahésion
def box_adherer(end=None):
if end:
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]))
if end != crans_utils.localized_datetime():
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]))
else:
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
@ -246,9 +243,8 @@ class Dialog(proprio.Dialog):
# Génération de la facture pour adhésion
def paiement(tag_paiement, adherent, finadhesion, comment, facture, cancel_cont, cont):
now = time.time()
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
now = crans_utils.localized_datetime()
new_finadhesion = max(finadhesion, now).replace(year=max(finadhesion, now).year + 1)
new_debutadhesion = now
if facture:
facture = self.conn.search(dn=facture.dn, scope=0, mode='rw')[0]
@ -260,8 +256,8 @@ class Dialog(proprio.Dialog):
facture['modePaiement'] = unicode(tag_paiement, 'utf-8')
facture['info'] = unicode(comment, 'utf-8')
facture['article'].append(config.cotisation.dico_adh)
facture["finAdhesion"] = unicode(new_finadhesion)
facture["debutAdhesion"] = unicode(new_debutadhesion)
facture["finAdhesion"] = new_finadhesion
facture["debutAdhesion"] = new_debutadhesion
# On peut retarder le credit pour ajouter des contribution pour la connexion internet à la facture
if crediter:
if self.confirm_item(item=facture,
@ -284,9 +280,13 @@ class Dialog(proprio.Dialog):
raise Continue(cont(adherent=adherent))
finadhesion = adherent.fin_adhesion()
now = crans_utils.localized_datetime()
try:
finadhesion = adherent.fin_adhesion().value
except AttributeError:
finadhesion = now
# Si fin de l'adhésion trop loin dans le futur, rien a faire
if finadhesion and finadhesion - config.cotisation.delai_readh > time.time():
if finadhesion and (finadhesion - now).days > config.cotisation.delai_readh_jour:
self.handle_dialog(cancel_cont if cancel_cont else cont, box_already, finadhesion)
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
def box(finconnexion, default_item=None):
t_end = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(finconnexion))
t_end = finconnexion
return self.dialog.menu(
"Connexion jusqu'au %s" % t_end if finconnexion else "N'a jamais été connecté",
"Connexion jusqu'au %s" % t_end if finconnexion != datetime.datetime.fromtimestamp(0, tz=pytz.utc) else "N'a jamais été connecté",
width=0,
height=0,
menu_height=0,
@ -357,17 +357,16 @@ class Dialog(proprio.Dialog):
# 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):
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
now = crans_utils.localized_datetime()
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:
t_end_adh = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(finadhesion))
t_end_conn = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(new_finconnexion))
if new_finconnexion - finadhesion > 30 * 3600 * 24:
if (new_finconnexion - finadhesion.value).days > 0:
t_end_adh = finadhesion.value
t_end_conn = finconnexion
if (new_finconnexion - finadhesion.value).days > 30:
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:
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" \
@ -377,8 +376,8 @@ class Dialog(proprio.Dialog):
if facture:
with self.conn.search(dn=facture.dn, scope=0, mode='rw')[0] as facture:
if mois:
facture["finConnexion"] = unicode(new_finconnexion)
facture["debutConnexion"] = unicode(new_debutconnexion)
facture["finConnexion"] = new_finconnexion
facture["debutConnexion"] = new_debutconnexion
facture["article"].append(config.cotisation.dico_cotis(mois))
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]),
@ -401,8 +400,8 @@ class Dialog(proprio.Dialog):
facture['modePaiement'] = unicode(tag_paiment, 'utf-8')
facture['article'].append(config.cotisation.dico_cotis(mois))
facture['info'] = unicode(comment, 'utf-8')
facture["finConnexion"] = unicode(new_finconnexion)
facture["debutConnexion"] = unicode(new_debutconnexion)
facture["finConnexion"] = new_finconnexion
facture["debutConnexion"] = new_debutconnexion
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),
title=u"Validation du paiement",
@ -412,7 +411,7 @@ class Dialog(proprio.Dialog):
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):
raise Continue(cancel_cont)
raise Continue(cont(adherent=adherent))
raise Continue(cont)
def todo_mois(tag, self_cont):
if tag == 'An':
@ -434,16 +433,18 @@ class Dialog(proprio.Dialog):
finconnexion = adherent.fin_connexion()
# Si l'adhésion fini avant la connexion
if finadhesion <= time.time() or finadhesion <= finconnexion:
if finadhesion <= crans_utils.localized_datetime() or finadhesion <= finconnexion:
if finadhesion:
t_end_adh = time.strftime('%d/%m/%Y %H:%M:%S', time.localtime(finadhesion))
# Si l'adhésion est déjà fini
if finadhesion <= time.time():
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)
if finadhesion <= crans_utils.localized_datetime():
if finadhesion == datetime.datetime.fromtimestamp(0, tz=pytz.utc):
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
elif finadhesion < 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" % (t_end_adh, t_end_conn), title="Réadhésion nécessaire", width=0, height=0, timeout=self.timeout)
t_end_conn = 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)
# É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)
@ -483,76 +484,6 @@ class Dialog(proprio.Dialog):
return self.proprio_choose_paiement(proprio=adherent, cont=self_cont, cancel_cont=lcont)
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):
a = attributs
attribs = [a.charteMA]
@ -616,6 +547,15 @@ class Dialog(proprio.Dialog):
with self.conn.search(dn=adherent.dn, scope=0, mode='rw')[0] as adherent:
for (key, values) in attrs.items():
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.history_gen()
adherent.save()
@ -653,6 +593,13 @@ class Dialog(proprio.Dialog):
if self.confirm_item(adherent, title="Créer l'adhérent suivant ?"):
adherent.validate_changes()
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:
adherent = None
return adherent
@ -916,13 +863,11 @@ class Dialog(proprio.Dialog):
"""Crée un adhérent et potentiellement son compte crans avec lui"""
def mycont(adherent=None, **kwargs):
if adherent:
# Une fois l'adhérent créé, on vois s'il donne sa carte étudiant et s'il adhére/prend la connexion internet
# Une fois l'adhérent créé, on vois s'il adhére/prend la connexion internet
#adh_cont = TailCall(self.modif_adherent, cont=cont, adherent=adherent)
conn_cont = TailCall(self.adherent_connexion, cont=cont(proprio=adherent), 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 = TailCall(self.adherent_etudes, cont=conn_cont, adherent=adherent)
etude_cont(cancel_cont=etude_cont)
carte_cont(cancel_cont=etude_cont)
# Comme on crée une facture, pas de retour possible
conn_cont(cancel_cont=conn_cont)
raise Continue(etude_cont)

View file

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

View file

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

View file

@ -12,6 +12,7 @@ if '/usr/scripts' not in sys.path:
import lc_ldap.objets as objets
import lc_ldap.attributs as attributs
import subprocess
import certificat
import blacklist
@ -34,10 +35,12 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
"""
a = attributs
# 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),
(a.portTCPout, 50), (a.portTCPin, 50), (a.portUDPout, 50),
(a.portUDPin, 50)
]
to_display = [(a.host, 30), (a.macAddress, 17), (a.ipHostNumber, 15)]
to_display_port = [(a.portTCPout, 50), (a.portTCPin, 50), (a.portUDPout, 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é
separateur = ' '
@ -58,15 +61,19 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
title="Paramètres machine",
backtitle="Gestion des machines du Crans")
def check_host(host, objectClass):
def check_host(host, objectClass, realm):
# Si c'est une machine wifi, host doit finir par wifi.crans.org
if "machineWifi" == objectClass or 'borneWifi' == objectClass:
if "machineWifi" == objectClass or 'borneWifi' == objectClass or realm == 'bornes':
hostend = ".wifi.crans.org"
# Si c'est une machine wifi, host doit finir par crans.org
elif "machineFixe" == objectClass:
elif "machineFixe" == objectClass or realm == 'serveurs':
hostend = ".crans.org"
# Si l'object class est machineCrans, pas de vérification
elif "machineCrans" == objectClass:
if realm == 'adm':
hostend = ".adm.crans.org"
if not '.' in host:
host = host + hostend
return host
# Sinon, libre à chachun d'ajouter d'autres objectClass ou de filtrer
# plus finement fonction des droits de self.conn.droits
@ -74,7 +81,7 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
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:
host = "%s.wifi.crans.org" % host
host = host + 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)
elif not host.endswith(hostend) and '.' in host:
@ -85,7 +92,8 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
def modif_machine(machine, attrs):
with self.conn.search(dn=machine.dn, scope=0, mode='rw')[0] as machine:
for (key, values) in attrs.items():
machine[key]=values
if values!=u'<automatique>' or key != 'ipHostNumber':
machine[key]=values
machine.validate_changes()
machine.history_gen()
machine.save()
@ -101,13 +109,18 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
}
with self.conn.newMachine(proprio.dn, realm, ldif) as machine:
for (key, values) in attrs.items():
machine[key]=values
if values!=u'<automatique>' or key != u'ipHostNumber':
machine[key]=values
if attributs.ipsec in machine.attribs:
machine[attributs.ipsec.ldap_name]=attributs.ipsec.default
machine.validate_changes()
if self.confirm_item(machine, "Voulez vous vraiement créer cette machine ?"):
machine.create()
self.display_item(machine, "La machine à bien été créée", ipsec=True)
self.display_item(machine, "La machine a 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
else:
raise Continue(cont)
@ -123,7 +136,7 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
values = [v for v in values.split(separateur) if v]
# Pour host, on fait quelques vérification de syntaxe
if a.ldap_name == 'host':
attrs[a.ldap_name]=check_host(values, objectClass)
attrs[a.ldap_name]=check_host(values, objectClass, realm)
else:
attrs[a.ldap_name]=values
# Soit on édite une machine existante
@ -134,10 +147,16 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
machine = create_machine(proprio, realm, attrs)
raise Continue(cont(machine=machine))
if machine:
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)
# On prépare les fiels à afficher à l'utilisateur si une erreure à lieu
@ -188,8 +207,8 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
menu_droits = {
'Information' : [a.parent, a.cableur, a.nounou],
'Autre': [a.parent, a.cableur, a.nounou],
'Blackliste':[a.cableur, a.nounou],
'Certificat': [a.parent, a.cableur, a.nounou],
'Blackliste':[a.nounou],
'Certificat': [a.parent, a.nounou],
'Exemption' : [a.nounou],
'Alias' : [a.parent, a.cableur, a.nounou],
'Remarques' : [a.cableur, a.nounou],
@ -251,24 +270,41 @@ class Dialog(certificat.Dialog, blacklist.Dialog):
menu_droits = {
'Fixe' : [a.soi, a.cableur, a.nounou],
'Wifi' : [a.soi, a.cableur, a.nounou],
'Appartements': [a.soi, a.cableur, a.nounou],
}
menu = {
'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'},
}
menu_order = ['Fixe', 'Wifi']
menu_order = ['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):
menu_droits.update({
'Fixe' : [a.nounou],
'Wifi' : [a.nounou],
'Wifi-v6' : [a.nounou],
'Adm' : [a.nounou],
})
menu.update({
'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-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'},
})
menu_order.append('Adm')
menu_order += ['Adm', 'Wifi-v6']
def box(default_item=None):
return self.dialog.menu(
"Type de Machine ?",

View file

@ -85,11 +85,10 @@ class Dialog(machine.Dialog, blacklist.Dialog):
@tailcaller
def set_password(proprio, update_obj, cont):
if self.dialog.yesno("Attribuer un mot de passe maintenant ?",
if self.dialog.yesno("Attribuer un mot de passe maintenant ? (Vous aurez la possibilité d'imprimer un ticket plus tard également ...)",
title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
timeout=self.timeout
) == 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))
if return_obj:
return proprio
@ -168,7 +167,7 @@ class Dialog(machine.Dialog, blacklist.Dialog):
raise Continue(cont(proprio=proprio))
#(code, passwords) = self.handle_dialog(cont, box)
(code, passwords) = (self.dialog.DIALOG_OK, "")
self_cont = TailCall(self.proprio_compte_password, proprio=proprio, cont=cont)
self_cont = TailCall(self.proprio_compte_password, proprio=proprio, cont=cont, return_obj=return_obj)
return self.handle_dialog_result(
code=code,
output=passwords,
@ -411,14 +410,19 @@ class Dialog(machine.Dialog, blacklist.Dialog):
"cheque" : "Chèque",
"carte" : "Par carte bancaire",
"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):
box_paiement_order = ["liquide", "cheque", "carte"]
box_paiement_order = ["liquide", "cheque", "carte","note"]
if "cransAccount" in proprio['objectClass']:
if not "SOLDE" in [art['code'] for art in articles] and proprio["solde"]:
box_paiement_order.append("solde")
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 = []
for key in box_paiement_order:
choices.append((key, box_paiement[key], 1 if key == tag else 0))
@ -457,6 +461,7 @@ class Dialog(machine.Dialog, blacklist.Dialog):
def box_choose_item(tags):
choices = []
gestion.config.factures.ITEMS.update(gestion.config.factures.ITEM_SOLDE)
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))
return self.dialog.checklist(
@ -499,11 +504,19 @@ class Dialog(machine.Dialog, blacklist.Dialog):
def paiement(have_set, tag, proprio, comment, cancel_cont, cont):
articles = copy.deepcopy(have_set)
# On formate les articles
for article in articles:
if article['pu'] == '*':
article['pu'] = article['nombre']
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:
facture['modePaiement']=unicode(tag, 'utf-8')
facture['article']=articles
@ -517,13 +530,13 @@ class Dialog(machine.Dialog, blacklist.Dialog):
arts = ["%s %s" % (art['nombre'], art['designation']) for art in facture['article'] if art['code'] != 'SOLDE']
if arts:
self.dialog.msgbox(
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",
width=0, height=0, timeout=self.timeout)
text=u"Vous pouvez remettre à l'adherent les articles (si ce sont des articles) suivant :\n * %s" % '\n * '.join(arts),
title=u"Vente terminée",
width=0, height=0, timeout=self.timeout)
if tag == "solde":
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)
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)
if [a for a in facture['article'] if art['code'] == 'SOLDE']:
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)
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)
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):
raise Continue(cancel_cont)
@ -553,12 +566,13 @@ class Dialog(machine.Dialog, blacklist.Dialog):
cancel_cont=cancel_cont,
error_cont=cancel_cont,
codes_todo=[([self.dialog.DIALOG_OK], paiement, [have_set, tag_paiment, proprio, comment_paiement, cancel_cont, cont])]
)
)
# Sinon, on propose des articles à chosir
else:
(code, tags) = self.handle_dialog(cont, box_choose_item, tags)
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(
code=code,
output=tags,

File diff suppressed because it is too large Load diff

View file

@ -176,14 +176,22 @@ class home:
### Redirection
if mail_redirect:
file(home + '/.forward', 'w').write(mail_redirect + '\n')
write_in_forward = True
# 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.chmod(home + '/.forward', 0604)
os.chmod(home + '/.forward', 0600)
### Owncloud dans le home
if not os.path.exists(home + '/OwnCloud'):
os.mkdir(home + '/OwnCloud')
os.chown(home + '/OwnCloud', int(uid), grp.getgrnam('www-data').gr_gid)
os.chmod(home + '/OwnCloud',0770)
os.chmod(home + '/OwnCloud', 0770)
except:
print ERREUR
if self.debug:

View file

@ -43,6 +43,7 @@ class autostatus(gen_config) :
"obm.crans.org",
"obm.adm.crans.org",
"batv-3.adm.crans.org",
"batv-1.adm.crans.org",
# Config par défaut
"non-configure.wifi.crans.org",
@ -70,6 +71,7 @@ class autostatus(gen_config) :
"ragnarok.crans.org", # RIP contrôleur disque...
"zamok.crans.org", # c'est en fait fx
"bati-2.adm.crans.org", # N'est plus en place
"batv-1.crans.org",
# Bornes wifi de test
"bullet5.wifi.crans.org",
@ -142,8 +144,10 @@ class autostatus(gen_config) :
infos_routeurs = {}
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.102' ] = ['Pioneer', 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' ] = ['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 [ '193.49.65.1' ] = ['RenaterCachan1' , u'Routeur Renater' ]
infos_routeurs [ '193.51.181.186' ] = ['RenaterCachan2', u'Routeur Renater']

View file

@ -73,6 +73,9 @@ class TLSA(ResourceRecord):
if not r_format in ['pem', '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] == '.':
try:
cert = ssl.get_server_certificate((name[:-1], port), ca_certs='/etc/ssl/certs/ca-certificates.crt')
@ -90,6 +93,7 @@ class TLSA(ResourceRecord):
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))
self.certhex = certhex
if compat:
super(TLSA, self).__init__(
'TYPE52',
@ -151,13 +155,17 @@ class TXT(ResourceRecord):
"""Entrée DNS pour un champ TXT"""
def __init__(self, name, value, ttl=None):
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):
"""Retourne une chaîne printable dans un fichier bind"""
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:
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):
"""Entrée DNS pour un alias (toto -> redisdead)"""
@ -390,13 +398,14 @@ class Zone(ZoneBase):
def add_tlsa_record(self, cert):
"""Ajout d'un certif dans le DNS"""
if 'TLSACert' in cert['objectClass']:
for host in cert['hostCert']:
nom = self.get_name(host)
if nom is None: continue
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'))
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'))
if not cert.get('revocked', [False])[0]:
for host in cert['hostCert']:
nom = self.get_name(host)
if nom is None: continue
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'))
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'))
def add_machine(self, machine):
"""Ajout d'une machine, à savoir chaînage d'ajout
@ -620,6 +629,12 @@ 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 = {
'crans.org': [
TXT(short_name(_mx), 'v=spf1 mx:crans.org ~all') for _mx in config.dns.MXs
@ -673,7 +688,7 @@ class dns(gen_config):
# On met les mêmes MX pour toutes les zones.
zone.extend(self.MXs)
# 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]:
for rr_type in [self.SRVs, self.NAPTRs, self.DSs, self.EXTRAS, self.SPFs, self.NON_CLONABLE_SPFs, self.DKIM]:
if zone.zone_name in rr_type.keys():
zone.extend(rr_type[zone.zone_name])
for m in machines:

View file

@ -45,9 +45,13 @@ class exemptions(gen_config):
for machine in machines:
for destination in machine["exempt"]:
if destination.value.version == 4:
if not machine['ipHostNumber']:
continue
source = str(machine["ipHostNumber"][0])
requete = "INSERT INTO exemptes (ip_crans, ip_dest) VALUES ('%s','%s')" % (source, destination)
else:
if not machine['macAddress']:
continue
source = str(machine["macAddress"][0])
requete = "INSERT INTO exemptes6 (mac_crans, ip_dest) VALUES ('%s','%s')" % (source, destination)
# Si ip vide, passons au suivant
@ -86,7 +90,8 @@ class machines(gen_config):
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))
elif m.proprio().__class__ == lc_ldap.objets.AssociationCrans:
curseur.execute("INSERT INTO machines (mac_addr, type, id) VALUES ('%s','crans',%s);" % (m['macAddress'][0], m['mid'][0].value))
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))
# on 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.ipset['mac_ip']={
'adh' : Ipset("MAC-IP-ADH","macipmap","--from 138.231.136.0 --to 138.231.151.255"),
'adm' : Ipset("MAC-IP-ADM","macipmap","--from 10.231.136.0 --to 10.231.136.255"),
'app' : Ipset("MAC-IP-APP","macipmap","--from 10.2.9.0 --to 10.2.9.255"),
'adh' : Ipset("MAC-IP-ADH", "bitmap:ip,mac", "range 138.231.136.0-138.231.151.255"),
'adm' : Ipset("MAC-IP-ADM", "bitmap:ip,mac", "range 10.231.136.0-10.231.136.255"),
'app' : Ipset("MAC-IP-APP", "bitmap:ip,mac", "range 10.2.9.0-10.2.9.255"),
}
self.ipset['blacklist']={
'hard' : Ipset("BLACKLIST-HARD","ipmap","--from 138.231.136.0 --to 138.231.151.255"),
'hard' : Ipset("BLACKLIST-HARD", "hash:ip"),
}
@ -110,7 +110,7 @@ class firewall(utils.firewall_tools) :
if fill_ipset:
# On récupère la liste de toutes les ips blacklistés hard
bl_hard_ips = self.blacklisted_ips(config.blacklist_sanctions, config.NETs['all'])
bl_hard_ips = self.blacklisted_ips(config.blacklist_sanctions)
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['hard'])
self.ipset['blacklist']['hard'].restore(bl_hard_ips)
print OK
@ -131,7 +131,7 @@ class firewall(utils.firewall_tools) :
def mac_ip_remove(self, mac, ip):
machine = {'macAddress':[mac], 'ipHostNumber': [ip]}
self.test_mac_ip_dispatch(lambda set, data: set.ipset['mac_ip'][set].delete(data), machine)
self.test_mac_ip_dispatch(lambda set, data: self.ipset['mac_ip'][set].delete(data), machine)
def test_mac_ip_dispatch(self, func, machine):
"""Détermine à quel set de mac-ip appliquer la fonction ``func`` (add, delete, append, ...)"""

View file

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

View file

@ -33,18 +33,21 @@ class firewall(base.firewall_routeur):
self.use_tc.extend([self.limitation_debit])
self.ipset['reseaux_non_routable'] = {
'deny' : base.Ipset("RESEAUX-NON-ROUTABLE-DENY","nethash"),
'allow' : base.Ipset("RESEAUX-NON-ROUTABLE-ALLOW","nethash"),
'deny' : base.Ipset("RESEAUX-NON-ROUTABLE-DENY", "hash:net"),
'allow' : base.Ipset("RESEAUX-NON-ROUTABLE-ALLOW", "hash:net"),
}
self.ipset['blacklist'].update({
'soft' : base.Ipset("BLACKLIST-SOFT","ipmap","--from 138.231.136.0 --to 138.231.151.255"),
'upload' : base.Ipset("BLACKLIST-UPLOAD","ipmap","--from 138.231.136.0 --to 138.231.151.255"),
'soft' : base.Ipset("BLACKLIST-SOFT", "hash:ip"),
'upload' : base.Ipset("BLACKLIST-UPLOAD", "hash:ip"),
})
# Portail captif/blacklist soft: ipset des gens ayant cliqué pour continuer à naviguer
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):
"""Mise à jour des blacklistes"""
self.blacklist_hard_maj(ips)
@ -129,6 +132,7 @@ class firewall(base.firewall_routeur):
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.blacklist_soft(table))
self.add(table, chain, '-j %s' % self.blacklist_hard(table))
chain = 'POSTROUTING'
self.add(table, chain, '-j %s' % self.connexion_wififederez(table))
@ -247,6 +251,7 @@ class firewall(base.firewall_routeur):
if table == 'nat':
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 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)
print OK
@ -342,7 +347,7 @@ class firewall(base.firewall_routeur):
if fill_ipset:
# On récupère la liste de toutes les ips blacklistés soft
bl_soft_ips = self.blacklisted_ips(base.config.blacklist_sanctions_soft, base.config.NETs['all'])
bl_soft_ips = self.blacklisted_ips(base.config.blacklist_sanctions_soft)
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['soft'])
self.ipset['blacklist']['soft'].restore(bl_soft_ips)
print OK
@ -367,6 +372,41 @@ class firewall(base.firewall_routeur):
self.apply(table, 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):
self.blacklist_upload(fill_ipset=True)
# for ip in ip_list:
@ -385,7 +425,7 @@ class firewall(base.firewall_routeur):
if fill_ipset:
# 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, base.config.NETs['all'])
bl_upload_ips = self.blacklisted_ips(base.config.blacklist_bridage_upload)
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['upload'])
self.ipset['blacklist']['upload'].restore(bl_upload_ips)
print OK
@ -453,6 +493,7 @@ class firewall(base.firewall_routeur):
if table == 'filter':
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 proto in base.config.firewall.srv_ports_default.keys():
if base.config.firewall.srv_ports_default[proto]['output']:
@ -543,17 +584,17 @@ class firewall(base.firewall_routeur):
utils.tc("class add dev %s parent 1: classid 1:1 "
"htb rate %s ceil %s" % (dev[int_key], uplink_speed, uplink_speed))
utils.tc("class add dev %s parent 1:1 classid 1:2 "
"htb rate %smbit ceil %smbit" % (dev[int_key], debit_max, debit_max))
"htb rate %smbit ceil %smbit" % (dev[int_key], debit_max[int_key], debit_max[int_key]))
# Classe par defaut
utils.tc('class add dev %s parent 1:2 classid 1:10 '
'htb rate %smbit ceil %smbit prio 1' % (dev[int_key], debit_max, debit_max))
'htb rate %smbit ceil %smbit prio 1' % (dev[int_key], debit_max[int_key], debit_max[int_key]))
utils.tc('qdisc add dev %s parent 1:10 '
'handle 10: sfq perturb 10' % dev[int_key])
# Classe par pour la voip
utils.tc('class add dev %s parent 1:2 classid 1:12 '
'htb rate %smbit ceil %smbit prio 0' % (dev[int_key], debit_max, debit_max))
'htb rate %smbit ceil %smbit prio 0' % (dev[int_key], debit_max[int_key], debit_max[int_key]))
utils.tc('qdisc add dev %s parent 1:12 '
'handle 12: sfq perturb 10' % dev[int_key])
@ -581,7 +622,7 @@ class firewall(base.firewall_routeur):
# Classe pour le download des apparetments
utils.tc("class add dev %s parent 1: classid 1:3 "
"htb rate %smbit ceil %smbit" % (dev[int_key], debit_max/10, debit_max/2))
"htb rate %smbit ceil %smbit" % (dev[int_key], debit_max['total']/10, debit_max['total']/2))
utils.tc('qdisc add dev %s parent 1:3 '
'handle 3: sfq perturb 10' % dev[int_key])
@ -605,7 +646,7 @@ class firewall(base.firewall_routeur):
# Classe pour le download wifi federez
utils.tc("class add dev %s parent 1: classid 1:5 "
"htb rate %smbit ceil %smbit" % (dev[int_key], debit_max/10, debit_max/2))
"htb rate %smbit ceil %smbit" % (dev[int_key], debit_max['total']/10, debit_max['total']/2))
utils.tc('qdisc add dev %s parent 1:5 '
'handle 3: sfq perturb 10' % dev[int_key])

View file

@ -1,11 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import netaddr
if '/usr/scripts/' not in sys.path:
sys.path.append('/usr/scripts/')
if '/usr/scripts' not in sys.path:
sys.path.append('/usr/scripts')
import syslog
import subprocess
@ -56,7 +55,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"""
def machines(self):
"""Renvois la liste de toutes les machines"""
"""Renvoit la liste de toutes les machines"""
if self._machines:
return self._machines
# On utilise allMachinesAdherents car on a besoin que
@ -64,48 +63,37 @@ class firewall_tools(object) :
# les blacklistes d'un proprio lorsque l'on regarde les blacklistes
# d'une machine
anim('\tChargement des machines')
self._machines, self._adherents = self.conn.allMachinesAdherents()
self._adherents = [ adh for adh in self._adherents if adh.paiement_ok() ]
# On prend toutes les machines y compris celles de ceux qui n'ont pas payé
# elles seront ajoutées dans mac_ip mais blacklistées du fait du non paiement ensuite
self._machines = self.conn.allMachines()
print OK
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):
"""Renvois la liste de toutes les machines ayant une blackliste actives"""
"""Renvoit la liste de toutes les machines ayant une blackliste actives"""
if self._blacklisted_machines:
return self._blacklisted_machines
self._blacklisted_machines = [ machine for machine in self.machines() if machine.blacklist_actif() ]
return self._blacklisted_machines
def blacklisted_ips(self, blacklist_sanctions=None, nets=None):
"""Renvois l'ensemble des ips des machines ayant une blacklist dans blacklist_sanctions et étant dans nets si spécifié"""
def blacklisted_ips(self, blacklist_sanctions=None):
"""Renvoit l'ensemble des ips des machines ayant une blacklist dans blacklist_sanctions et étant dans nets si spécifié"""
bl_ips = set()
for machine in self.blacklisted_machines():
if blacklist_sanctions is None or set(bl['type'] for bl in machine.blacklist_actif()).intersection(blacklist_sanctions):
for ip in machine['ipHostNumber']:
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))
bl_ips.add(str(ip))
return bl_ips
def blacklisted_adherents(self, excepts=[]):
"""Renvois la liste de tous les adhérents ayant une blackliste active en ignorant les blacklist de excepts"""
"""Renvoit 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):
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)
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')
for net in base.config.NETs['all']:
self.add(table, chain, '-d %s -j RETURN' % net)
for adh in self.blacklisted_adherents():
for adh in self.blacklisted_adherents(excepts=['paiement']):
if 'uidNumber' in adh:
self.add(table, chain, '-m owner --uid-owner %s -j REJECT' % adh['uidNumber'][0])
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 )
# 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' % dev_ip6)
ip6tables.filter.forward('-o %s -p tcp -d 2a00:1450:4006::/32 -j REJECT --reject-with icmp6-addr-unreachable' % dev_ip6)
# 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)

View file

@ -64,10 +64,7 @@ class base_reconfigure:
'warez':__service_develop['blacklist_warez'],
'ipv6_ra':__service_develop['blacklist_ipv6_ra'],
'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_p2p': __service_develop['blacklist_autodisc_p2p'],
'bloq': __service_develop['blacklist_bloq'],
})
except ImportError:

View file

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

View file

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

View file

@ -2,14 +2,12 @@
# -*- coding: utf-8 -*-
"""
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 :
* mot de passe admin (password manager user-name <username>)
* activation du ssh (crypto key generate ssh)
* copie fichier de conf
pour les reconfiguration copier le fichier de conf
pour les reconfiguration copier le fichier de conf
dans /cfg/startup-config
Dans tous les cas FAIRE LE SNMP A LA MAIN (script hptools)
@ -73,13 +71,13 @@ def net_of_vlan_name(name):
class Port(object):
"""Un port de switch"""
num = None
# : uplink: None ou str
uplink = None
# : Liste de serveurs
servers = None
# : Liste de bornes
bornes = None
@ -100,7 +98,7 @@ class Port(object):
self.chambres = list()
self.seen_macs = list()
self.seen_vlans = list()
def __str__(self):
if self.uplink:
return self.uplink
@ -139,7 +137,7 @@ class Port(object):
if any( adh.get('droits', None) for adh in self.adherents()):
return ''
return 'speed-duplex auto-10-100'
def flowcontrol(self):
"""Est-ce que le flowcontrol est activé sur ce port ?"""
if self.uplink or self.servers:
@ -190,11 +188,11 @@ class Port(object):
# l'auth radius
else:
return V_NO
def radius_auth(self):
"""Doit-on faire de l'auth radius ?"""
return not self.uplink and not self.servers and not self.bornes
def adherents(self):
"""Adhérents sur la prise"""
filtre = u'(|%s)' % (''.join('(chbre=%s)' % c for c in self.chambres))
@ -227,7 +225,7 @@ class PortList(list):
"""
liste = list(int(x) for x in self)
liste.sort()
sortie = []
groupe = [-99999, -99999]
for x in itertools.chain(liste, [99999]):
@ -270,7 +268,7 @@ def get_port_dict(switch):
port.servers.append(machine)
elif classe == 'borneWifi':
port.bornes.append(machine)
# On remplit les chambres
for prise, chbres in annuaire.reverse(bat).iteritems():
# TODO rajouter un arg à reverse
@ -349,7 +347,7 @@ def check_conf_ldap(hostname):
# La chambre est inconnue -> drop
continue
th_prises_set.add(th_prise)
pr_prise = bat.lower() + '%d%02d' % (sw_num, port.num)
if th_prises_set and pr_prise not in th_prises_set:
print(" Aucune machine de chbre. Candidats: %r" % th_prises_set)
@ -402,7 +400,7 @@ def format_prises_group(data, first, last):
prises. Entre first et last"""
first = (first-1)/2*2+1
last = (-last/2)*-2
def align5(txt, right=False):
"""Aligne le texte en limitant à 5 char"""
if len(txt) > 5:
@ -443,7 +441,11 @@ def format_prises_group(data, first, last):
def pretty_print(hostname):
"""Affiche joliement le plan de connexion d'un switch"""
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)
total = max(port_dict.keys())
@ -463,8 +465,12 @@ def conf_switch(hostname):
"""Affiche la configuration d'un switch"""
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]
tpl_env = jinja2.Environment(loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
##for info:
tpl_env.filters['vlan_id'] = vlan_id
@ -482,9 +488,6 @@ def conf_switch(hostname):
],
'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_rid_servers': [34, 160],
@ -519,7 +522,25 @@ def conf_switch(hostname):
for rid in data['dhcp_rid_servers']:
first = netaddr.IPNetwork(net_of_vlan_name(vname)[0]).first
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
if "2620" in switch['info'][0].value:
data['ra_filter'] = True
@ -529,7 +550,7 @@ def conf_switch(hostname):
# Switch avec des ports gigabit uniquement
if imodel in GIGABIT_MODELS:
data['gigabit'] = True
# Build ports !
ports_list = PortList(get_port_dict(switch).itervalues())
data['ports'] = ports_list
@ -548,9 +569,14 @@ def conf_switch(hostname):
V_NO: 'no'}[assign]
vlan.setdefault(attr, PortList())
vlan[attr].extend(p)
if name == 'adm':
if name == 'adm' and not data['public']:
vlan['ip_cfg'] = (gethostbyname(hostname), '255.255.255.0')
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
vlan['extra'] = 'ip igmp\nno ip igmp querier'
vlans[name] = vlan
@ -581,7 +607,7 @@ if __name__ == "__main__":
help="Affiche un tableau ascii du plan de connexion du switch")
options = parser.parse_args(sys.argv[1:])
if options.check:
check_conf_ldap(options.hostname)
elif options.pretty:

View file

@ -29,8 +29,10 @@ from whos import aff
import signal
import getopt
from time import strftime, strptime, localtime, mktime, time
import datetime
from dateutil.relativedelta import relativedelta
import re
import subprocess
import affich_tools
import config
import config.cotisation as cotisation
@ -39,7 +41,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 Adherent, AssociationCrans, Club, Facture
from ldap_crans import Machine, MachineFixe, MachineWifi, MachineCrans, BorneWifi
from ldap_crans import tz, generalizedTimeFormat, fromGeneralizedTimeFormat
from ldap_crans import tz, generalizedTimeFormat, fromGeneralizedTimeFormat, datetimeFromGTF, datetimeToGTF, localizedDatetime
import user_tests
isadm = user_tests.isadm()
@ -52,6 +54,8 @@ iscontroleur = u'Tresorier' in droits
isbureau = u'Bureau' in droits
encoding = sys.stdin.encoding or 'UTF-8'
NAISSANCE_RE = re.compile(r"(?P<jour>[^ ]*)/(?P<mois>[^ ]*)/(?P<annee>[^ ]*)")
if u'Nounou' in droits:
# Si on est nounou
if os.path.exists(os.path.expanduser('~/.dialogrc')):
@ -101,22 +105,30 @@ def set_bases(adher):
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'"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
annul, result = dialog(arg)
if annul:
if annul or any([result[i] == '' for i in xrange(len(result))]):
return 1
# Traitement
err = ''
try: adher.nom(result[0])
except ValueError, c: err += c.args[0] + '\n'
try:
adher.nom(result[0])
except ValueError, c:
err += c.args[0] + '\n'
try: adher.prenom(result[1])
except ValueError, c: err += c.args[0] + '\n'
try:
adher.prenom(result[1])
except ValueError, c:
err += c.args[0] + '\n'
try: adher.tel(result[2])
except ValueError, c: err += c.args[0] + '\n'
try:
adher.tel(result[2])
except ValueError, c:
err += c.args[0] + '\n'
# Un adhérent du même nom existe-t-il déjà ?
req = 'nom=' + result[0] + '&prenom=' + result[1]
@ -128,6 +140,25 @@ def set_bases(adher):
if no:
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])
# Des erreurs ?
@ -213,6 +244,7 @@ def set_etudes(adher):
arg += u'"Maximilien Sorre" "" '
arg += u'"Gustave Eiffel" "" '
arg += u'"EFREI" "" '
arg += u'"ESIGETEL" "" '
arg += u'"ESTP" "" '
arg += u'"P1" "Université Panthéon Sorbonne" '
arg += u'"P2" "Université Panthéon Assas" '
@ -424,9 +456,12 @@ def set_contact(adher):
if result[0].split()[0] == 'Laisser':
break
elif result[0].split()[1] == u'un':
if not set_compte(adher): break
if not set_compte(adher):
set_mail_ext(adher)
break
else:
if not set_mail(adher): break
if not set_mail(adher):
break
def set_mail(adher):
"""Demande l'adresse mail extérieure d'un adhérent
@ -794,7 +829,13 @@ def del_adher(adher):
arg += u'--msgbox "Le commentaire est obligatoire\n\n\n" 0 0'
dialog(arg)
adher.delete(res[0])
try:
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'--msgbox "Adhérent détruit\n\n\n" 0 0'
@ -1049,18 +1090,18 @@ def set_solde(clas):
annul, comment = dialog(arg)
if not annul:
if comment[0]:
if comment:
comment = comment[0]
else:
comment = None
comment = ''
f = Facture(clas)
f.ajoute({'nombre': 1, 'code':'SOLDE', 'designation': "Modification du solde par un imprimeur. Moyen de paiement: %s, remarque: %s" % (_mode, comment), 'pu': _montant})
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.modePaiement(_mode.lower())
try:
# Met aussi à jour le solde.
f.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
f.recuPaiement(datetimeToGTF(localizedDatetime()))
f.save()
db.services_to_restart('mail_solde', [
'%s a fait %s euros pour %s [mode: %s, remarque: %s]' %
@ -1074,6 +1115,7 @@ def set_solde(clas):
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(),)
dialog(arg)
break
def set_vente(proprio):
u"""
@ -1161,6 +1203,7 @@ def set_vente(proprio):
menu.append(u'"Spc" "Espèces" ')
menu.append(u'"Chq" "Chèque" ')
menu.append(u'"Cb" "Carte bancaire" ')
menu.append(u'"Note" "Note Kfet (attention, moins traçable)" ')
if isimprimeur and proprio.solde() - f.total() > 0:
menu.append(u'"Sol" "Solde Crans (actuel : %s€)" ' % (proprio.solde()))
@ -1187,6 +1230,10 @@ def set_vente(proprio):
f.modePaiement('cheque')
paiement = u"Chèque"
annul, comment = dialog(arg)
elif result[0] == "Note":
f.modePaiement('note')
paiement = u"Note"
annul, comment = dialog(arg)
elif result[0] == "Sol" and isimprimeur:
f.modePaiement('solde')
paiement = u"Solde Crans"
@ -1210,8 +1257,15 @@ def set_vente(proprio):
return 1
else:
try:
f.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
f.recuPaiement(datetimeToGTF(localizedDatetime()))
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'--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'])
@ -1250,9 +1304,26 @@ def confirm(clas):
return 1
try:
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:
in_facture.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
in_facture.recuPaiement(datetimeToGTF(localizedDatetime()))
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:
arg = u'--title "Enregistrement" '
arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(unicode(c.args[0]))
@ -1260,9 +1331,6 @@ def confirm(clas):
return 1
in_facture = None
cprint(res)
affich_tools.prompt(u"Appuyez sur ENTREE pour continuer")
def set_blackliste(clas):
u""" Édite ou ajoute un item de la blackliste """
bl = clas.blacklist()
@ -1494,6 +1562,9 @@ def set_adhesion(proprio):
facture.modePaiement(_mode.lower())
break
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):
"""Maj de la période d'accès de l'adhérent"""
@ -1501,12 +1572,16 @@ 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.
adhEnd = proprio.adhesion()
if in_facture is not None:
adhEnd = max(adhEnd, fromGeneralizedTimeFormat(in_facture._data.get('finAdhesion', ["19700101000000Z"])[0]))
if adhEnd < time():
if adhEnd - cotisation.delai_readh < time():
stat = set_adhesion(proprio)
if stat == 1:
if stat == 1 and adhEnd < time():
return 1
if in_facture is not None:
adhEnd = max(adhEnd, fromGeneralizedTimeFormat(in_facture._data.get('finAdhesion', ["19700101000000Z"])[0]))
@ -1521,7 +1596,7 @@ def set_connexion(proprio):
while True:
args = u'--title "Connexion de %s" ' % proprio.Nom()
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"),)
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())),)
else:
args += u'--menu "Connexion actuellement inactive, choisir une durée. : " 0 0 0 '
args += u'"An" "Prolonger d\'un an." '
@ -1531,7 +1606,8 @@ def set_connexion(proprio):
annul, res = dialog(args)
if annul:
in_facture.supprime(pop=True)
if in_facture is not None:
in_facture.supprime(pop=True)
return 1
res = res[0]
if res == "An":
@ -1581,17 +1657,17 @@ def set_connexion(proprio):
newEnd = fromGeneralizedTimeFormat(facture._data.get('finConnexion', ["19700101000000Z"])[0])
if newEnd > adhEnd:
arg = u'--title "Avertissement" '
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)), )
no, res = dialog(arg)
if no:
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)
annul, res = dialog(arg)
if annul:
facture._set('finConnexion', [])
facture._set('debutConnexion', [])
facture.supprime(pop=True)
continue
break
if not facture.modePaiement():
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'"Cheque" "Par chèque : ne pas oublier de vérifier signature, date, ordre et montant." '
arg += u'"Carte" "Par CB : tromboner le ticket." '
@ -1618,6 +1694,10 @@ def set_connexion(proprio):
break
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
@ -1778,7 +1858,7 @@ def set_facture_recu(facture):
if annul:
return 1
if u"Pmt" in res:
facture.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
facture.recuPaiement(datetimeToGTF(localizedDatetime()))
else:
facture.recuPaiement(False)
@ -2104,8 +2184,10 @@ def new_adher(adher):
4 etapes :
* set_bases
* set_etudes
* set_admin
* set_adhesion
* set_connexion
* set_contact
(qui appelle set_mail_ext si on met un compte crans)
* set_rque
Retourne 1 si annulation.
"""
@ -2117,15 +2199,16 @@ def new_adher(adher):
set_etudes,
set_adhesion,
set_connexion,
set_admin,
set_contact,
set_mail_ext,
set_rque,
]
step = 0
while step < len(steps):
if steps[step](adher): step -= 1
if steps[step](adher):
if step == 0:
return 1
step -= 1
else: step += 1
if not confirm(adher): break
@ -2155,7 +2238,8 @@ def modif_adher(adher):
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'"Adhesion" "Pour toute réadhésion *sans* connexion." '
arg += u'"Administratif" "Pour renseigner la fournitire de la charte des MA." '
if isadm or isbureau:
arg += u'"Administratif" "Pour renseigner la fourniture de la charte des MA." '
arg += u'"Etat-civil" "Nom, prénom" '
if adher.chbre() == 'EXT':
arg += u'"Adresse" "Déménagement" '
@ -2167,7 +2251,7 @@ def modif_adher(adher):
arg += u'"Mail" "Créer un compte ou changer l\'adresse mail de contact" '
if 'cransAccount' in adher._data['objectClass']:
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'"Remarque" "Ajouter ou modifer un commentaire" '
if isadm or isbureau:
@ -2179,7 +2263,7 @@ def modif_adher(adher):
arg += u'"Shell" "Changer le shell de cet utilisateur" '
if isdeconnecteur:
arg += u'"Blackliste" "Modifier la blackliste de cet adhérent" '
if isimprimeur:
if isimprimeur and adher.compte():
arg += u'"Solde" "Effectuer un débit/crédit pour cet adhérent" '
arg += u'"Vente" "Vendre un cable ou adaptateur ethernet ou autre" '
@ -2236,13 +2320,15 @@ def modif_adher(adher):
arg += u'--msgbox "Vous n\'avez pas les droits necessaires pour effectuer cette opération.\n\n\n" 0 0'
dialog(arg)
return modif_adher(adher)
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'\nDoit-on continuer ?" 0 0'
no, res = dialog(arg)
if no: return modif_adher(adher)
for m in adher.machines():
m.delete("Depart du campus")
if no:
return modif_adher(adher)
try:
adher.delete("Depart du campus")
except EnvironmentError, c:

View file

@ -1,5 +1,19 @@
#!/usr/bin/env python2.7
# -*- 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

View file

@ -1,5 +1,19 @@
#!/bin/bash /usr/scripts/python.sh
# -*- 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
module"""

View file

@ -1,5 +1,19 @@
#!/bin/bash /usr/scripts/python.sh
# -*- 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
dans le module hptools"""

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