Compare commits

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

532 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
Pierre-Elliott Bécue
a09ef77d22 Droppe ce qui concerne la carte d'étudiant, et les blacklistes obsolètes. 2015-07-16 17:54:44 -06:00
Hamza Dely
1c205fca93 [mail] Nouveaux templates de mails pour les déménagements foireux 2015-07-15 22:10:56 +02:00
Pierre-Elliott Bécue
67bf2363cf Whos aussi doit oublier les histoires de paiement et compagnie. 2015-07-15 21:16:35 +02:00
Cron Daemon
14a1905de3 [ethercodes.dat] Mise à jour du fichier vendeur 2015-07-15 17:10:24 +02:00
Pierre-Elliott Bécue
5c3045d5cb On jette tout ce qui concerne paiement (adh glissantes en place depuis plus d'un an) 2015-07-15 09:09:17 -06:00
Daniel STAN
6499e6802a robots.txt: rajoute toujours / initial 2015-07-13 09:33:09 +02:00
Daniel STAN
d3677b832a whos: retire mention brassage 2015-07-12 12:22:07 +02:00
Vincent Le Gallic
e23b18c356 tiret tiret *espace* entrée. 2015-07-12 08:05:45 +02:00
Daniel STAN
ec94ee2e1d mail: send_template: inverse to et from 2015-07-08 15:06:17 +02:00
Daniel STAN
6b63e030a6 whosthere: proprification (PEP8 et cie) 2015-07-03 18:27:05 +02:00
Daniel STAN
6b64af3314 python.sh: mention du mode test dans testing.sh 2015-07-03 18:07:12 +02:00
Daniel STAN
7ca15d7527 sip: se connecte à pgsql.v4 2015-07-03 18:07:12 +02:00
Daniel STAN
32136b25fa mail: send_template(): adh est optionnel 2015-07-03 13:54:31 +02:00
Gabriel Detraz
12e755fa64 Adaptation à jessie sur les tty 2015-07-02 20:10:57 +02:00
Pierre-Elliott Bécue
607c1b8855 Oubli de modification du nom de chaîne pour le parefeu v4 2015-07-01 11:50:43 +02:00
Pierre-Elliott Bécue
7ab4485d54 On sépare les compteurs forwarding des compteurs input en ssh 2015-07-01 11:38:25 +02:00
Daniel STAN
372aa16c4f wiki/PagesPerso: connexion ro à django 2015-06-30 16:51:11 +02:00
Daniel STAN
971abfd950 ldap_crans.py: rationalise ip() 2015-06-30 01:08:04 +02:00
Daniel STAN
1df794db38 annuaires_pg: utilise db django 2015-06-30 00:45:23 +02:00
Daniel STAN
937aab00af creer_compte_wiki: creer_compte renvoie un tuple 2015-06-29 18:15:22 +02:00
Lucas Serrano
9788cadfec [usr-scripts/wiki] Correction typo ipv6 vo 2015-06-29 17:40:02 +02:00
Daniel STAN
7762315e30 autostatus: s/komaz/odlyd/ 2015-06-29 01:09:33 +02:00
Daniel STAN
8c8ac24c54 annuaires: vire mentions câblage crans-crous 2015-06-29 01:09:13 +02:00
Gabriel Detraz
2bcce4911f Quand une facture est controlée, on affichel'erreur au lieu de planter gest_crans, en attendant une solution définitive 2015-06-29 00:22:13 +02:00
Pierre-Elliott Bécue
dc37719f0d Mail pour l'AGE avec le pdf des deux textes.
* Bouh, du binaire !
2015-06-26 01:38:20 +02:00
Pierre-Elliott Bécue
2bbdfc7fc9 On structure correctement les emails envoyés pour y ajouter des PJ.
* Le message est multipart/mixed
 * Les variantes du texte sont contenues dans un multipart/alternative
 * Les PJ sont directement dans le multipart/mixed
2015-06-26 00:58:06 +02:00
Daniel STAN
3214dd0213 testing.sh: DBG_QUOTA pour serveur pg alternatif 2015-06-25 17:13:26 +02:00
Pierre-Elliott Bécue
a416c24e22 On met les infos modif et moyen de paiement dans le descriptif de la facture, pour l'historique de l'adh. 2015-06-23 17:59:07 +02:00
Pierre-Elliott Bécue
cef0e444ca Mode de paiement arbitraire toléré. 2015-06-23 17:54:06 +02:00
Pierre-Elliott Bécue
2842b24649 Ajout d'un moyen de paiement arbitraire pour le solde. 2015-06-23 17:52:23 +02:00
Pierre-Elliott Bécue
7b56a15685 La modification du solde crée une facture, et on enlève solde de vente. 2015-06-23 17:25:12 +02:00
Charlie Jacomme
a6c38a88bf Correction d'erreurs dans la version anglaise de demenagement 2015-06-23 13:20:06 +02:00
Gabriel Detraz
2f815c63bb On proprifie la fonction qui vérifie la validité d'un montant de facture 2015-06-23 12:16:17 +02:00
Gabriel Detraz
e53877f8b2 Si il n'y a pas de compte crans, on ne peut modifier le solde 2015-06-22 16:14:23 +02:00
Daniel STAN
3965df79d0 filter_hp: typo 2015-06-22 12:14:20 +02:00
Pierre-Elliott Bécue
351565faaf On compatibilise git-notify avec les urls gitlab.
* Il faut penser à changer l'attribut baseurl
 * Il faut ajouter isgitlab = 1
 * Il faut préciser gitlabowner = membres-actifs ou gitlabowner = nounous

Etc etc.
2015-06-22 03:43:01 +02:00
Vincent Le Gallic
18b7e20efb Depuis le temps que je demande à ce que ce mail soit signé 2015-06-22 02:14:41 +02:00
Vincent Le Gallic
d501143825 Typo 2015-06-21 20:34:01 +02:00
Daniel STAN
265e5d6b2d impression: débite l'adh et non le club (oups) 2015-06-19 17:51:13 +02:00
Daniel STAN
deeafe477e quota: sudo jamais interactif 2015-06-18 22:27:51 +02:00
Daniel STAN
36613a1dc7 Merge branch 'master' of https://gitlab.adm.crans.org/nounous/scripts 2015-06-17 11:03:46 +02:00
Daniel STAN
bbf4ad6ade testing.sh: peut indiquer un host ldap et/ou pg 2015-06-17 10:18:02 +02:00
Daniel STAN
85785c1f80 services.py kludge si absent de /etc/crans/ 2015-06-17 10:02:19 +02:00
Cron Daemon
71bc9bbc54 [ethercodes.dat] Mise à jour du fichier vendeur 2015-06-15 02:11:55 +02:00
Daniel STAN
fd86e567a9 filter_hp: début de proprification 2015-06-12 13:14:40 +02:00
Daniel STAN
a16ab6f9d7 fin_connexion --> www.crans.org/PermanencesCrans 2015-06-12 12:38:41 +02:00
Pierre-Elliott Bécue
65350221da Clarification de la doc. 2015-06-06 13:10:10 +02:00
Pierre-Elliott Bécue
1e62621fcc En v6 aussi, on met un ttl faible pour le forwarding 2015-06-06 01:16:59 +02:00
Pierre-Elliott Bécue
6740e96610 On laisse un ttl plus faible pour le forwarding en ssh 2015-06-06 01:15:54 +02:00
Pierre-Elliott Bécue
a70205526f On durcit avec du lubrifiant un peu le parefeu pour le bruteforce ssh. 2015-06-06 01:03:26 +02:00
Gabriel Detraz
2838a60501 On ajoute une option bornev6, c'est zoli, et on previent la nounou qui ajoute une machine v6 only 2015-06-04 23:25:39 +02:00
Gabriel Detraz
9da0007d40 ON met la bonne plage de rid pour les bornes v6 2015-06-04 22:39:24 +02:00
Pierre-Elliott Bécue
21c079865f PEP8 (au moins pour les espaces) 2015-06-03 13:42:22 +02:00
Gabriel Detraz
4045539f69 On ajoute la liste des bats dans config 2015-06-03 13:18:08 +02:00
Gabriel Detraz
689a0cd152 On donne comme vlanid 22 a federez wifi (modifie firewall, swicths2 et dhcp config) 2015-06-03 01:30:43 +02:00
Pierre-Elliott Bécue
5b8aca460e Je m'appelle Pierre-Elliott Bécue (et je suis le fils de Cap'tain Obvious) 2015-06-02 21:01:51 +02:00
Pierre-Elliott Bécue
1dde48ff32 Légère optimisation de la recherche d'un rid libre 2015-06-02 20:59:03 +02:00
Pierre-Elliott Bécue
6534241650 Deconnexion.py ne sert plus 2015-06-02 20:58:54 +02:00
Gabriel Detraz
877137942a On ne reattribu pas de nouveau rid si ip=auto et qu'il y en a deja un (ex : bornev6) 2015-06-02 17:38:11 +02:00
Gabriel Detraz
77558c1f25 Ajout de who4j dans les commandes par defaut 2015-06-01 22:00:45 +02:00
Daniel STAN
77d98bf3fa ldap_crans: fix encoding dans les factures 2015-06-01 18:53:45 +02:00
Gabriel Detraz
a800a65793 On ajoute un who4J 2015-06-01 13:34:04 +02:00
Gabriel Detraz
e4857523d0 On a changé le nom des xml wifi 2015-06-01 13:18:00 +02:00
Valentin Samir
9078c80d34 [sip] Utilisation de #!/bin/bash /usr/scripts/python.sh sur les scripts 2015-05-29 19:50:29 +02:00
Gabriel Detraz
7c98e01e56 Numero disponible -> archives 2015-05-27 18:16:45 +02:00
Gabriel Detraz
ec7f6d392f Oubli d'une ip, dans le range (plage(rid)) 2015-05-27 18:08:12 +02:00
Gabriel Detraz
729be231ee Enlève import numdisp, correction de bugs. 2015-05-25 13:39:12 +02:00
Gabriel Detraz
8745a89176 Modification de l'attribution des ip, on n'utilise plus numerodisp, on cherche par rid dispo 2015-05-25 12:51:27 +02:00
Gabriel Detraz
1d82e0c95c Correction de bug : les ipv4 privées doivent aussi etre renvoyées par ridtools 2015-05-25 01:09:47 +02:00
Daniel STAN
65760e5ddb ridtools: ne s'embête plus avec un "reste" de rid 2015-05-23 21:33:03 +02:00
Pierre-Elliott Bécue
08007c623e On remet bcfg2_reports et bcfg2-graph, et on renomme le nouveau dossier 2015-05-23 16:20:37 +02:00
Pierre-Elliott Bécue
1df9d480af Vieux plugin Python aux archives 2015-05-23 16:11:53 +02:00
Daniel STAN
3fb338e937 factures: ajoute les items pulls 2015-05-23 13:15:44 +02:00
Daniel STAN
8b3832355e factures: designation est un unicode (si possible) 2015-05-23 13:15:18 +02:00
Pierre-Elliott Bécue
d23bda8bd2 Mise à jour du plugin 2015-05-18 23:28:19 +02:00
Pierre-Elliott Bécue
8cc8770ee4 Ditry dépôt is dirty 2015-05-18 22:36:09 +02:00
Pierre-Elliott Bécue
0a580381cf On s'assure que l'ip est bien une netaddr.IPAddress 2015-05-17 17:58:09 +02:00
Pierre-Elliott Bécue
d81637eca6 Ajout de batb-5 2015-05-17 17:52:53 +02:00
Cron Daemon
03519e4acd [ethercodes.dat] Mise à jour du fichier vendeur 2015-05-15 02:11:52 +02:00
Gabriel Detraz
887d54c133 AJout de la maitrise de la bp sur federez-wifi 2015-05-14 16:44:31 +02:00
Gabriel Detraz
bad450fe3c Firewall : nat du vlan 12 derrier l'ip wififederez, ca marche 2015-05-14 10:12:43 +02:00
Gabriel Detraz
04b91024d9 Dhcp snooping sur le vlan 12 + dns sur vlan 12 2015-05-14 03:33:32 +02:00
Gabriel Detraz
ddfc320741 Nouveau vlan federez, id vlan 12 sur les switchs 2015-05-13 14:49:50 +02:00
Gabriel Detraz
00b24a8a3d On accepte ce qui vient de la mac d'odlyd et pas de komaz... 2015-05-11 16:50:11 +02:00
Gabriel Detraz
b1df8cfc19 Bug : google.com a pas cette ip .... 2015-05-11 15:44:13 +02:00
Valentin Samir
9b0bf76621 [pxeboot] Mise à jour ubuntu, ubuntu live cd et fedora 2015-05-10 19:16:37 +02:00
Valentin Samir
a94d180cde [pxeboot] Changement du format d'url pour fedora 2015-05-10 19:16:16 +02:00
Valentin Samir
57d4e52395 [pxeboot] Pas d'installer kfreebsd pour jessie 2015-05-10 19:15:50 +02:00
Pierre-Elliott Bécue
f67d38baee Première version de la refonte du plugin bcfg2 2015-05-10 15:25:46 +02:00
Gabriel Detraz
7e5cd649eb Alignement des normes : crans et plus crans.org 2015-05-08 20:29:28 +02:00
Gabriel Detraz
d394c688b6 Pour federez wifi, on enlève le suffixe @.... 2015-05-08 19:06:38 +02:00
Gabriel Detraz
7dac00f8b3 Ajout de la règle pour federez wifi, if @ in id 2015-05-08 16:45:16 +02:00
Gabriel Detraz
edde503c9e Patch propre serveurs-proxy federez dans auth.py 2015-05-01 01:46:45 +02:00
Pierre-Elliott Bécue
c1660df7ae Réadhésion possible jusqu'à 28 jours à l'avance. 2015-04-28 16:11:17 +02:00
Pierre-Elliott Bécue
3e90c78c16 Import relatif pour que ça marche avec bcfg2 2015-04-28 16:10:41 +02:00
Pierre-Elliott Bécue
b65466de08 Quand un switch n'existe pas, il faut planter. 2015-04-25 18:14:29 +02:00
Daniel STAN
4205d73132 auth.py: proprification légère 2015-04-24 13:48:46 +02:00
Pierre-Elliott Bécue
a0f0c80ead Version plus pythonesque de HPTools. Pleinement fonctionnelle sous jessie.
* Les requêtes de type lecture seule marchent très bien tout court ;
 * Celles de type écriture sont sans effet sous wheezy. C'est a priori
 un bug dans python-netsnmp
2015-04-24 01:55:52 +02:00
Pierre-Elliott Bécue
cd5ae8aaa5 Commit identité 2015-04-24 01:53:27 +02:00
Valentin Samir
831cebb84d [bind] SRV record pour jabber.crans.org
Certain vieux utilisent des JID en @jabber.crans.org et sans les SRV on retrouve :
"timed out on request for "jabber.crans.org" IN SRV. You should check your DNS configuration."
dans les logs.
2015-04-22 18:45:25 +02:00
Pierre-Elliott Bécue
a3bf7c7349 Les clubs n'ont pas forcément de login/mail.
* Et il leur faut un update_connexion, même s'il ne fait rien
2015-04-20 21:26:13 +02:00
Daniel STAN
0bdd06d8f4 config: ajoute 'comnpay' aux moyens de paiement
Seulement pour test, pour le moment.
2015-04-20 17:48:12 +02:00
Gabriel Detraz
3b3e554509 Support de ra guard sur les procurve 2620 2015-04-20 12:42:52 +02:00
Daniel STAN
73aa1fe7eb autostatus: fallback quand traceroute bizarre 2015-04-17 19:57:00 +02:00
Daniel STAN
18bcbd19ea deconnexion2: fusionne mails notif upload_hard
Et on peut ainsi virer ce machin de templating dans config/
2015-04-17 18:35:22 +02:00
Cron Daemon
0d1eca2f8c [ethercodes.dat] Mise à jour du fichier vendeur 2015-04-15 02:12:06 +02:00
Gabriel Detraz
b5990eaa47 Ecriture d'un backend cups pdf-> pcl avec options 2015-04-14 00:00:45 +02:00
Pierre-Elliott Bécue
bf98167a63 Un peu de ménage, et ajout d'une option pour whoser dans la base de test 2015-04-13 02:24:54 +02:00
Daniel STAN
d6bb56dfaa deconnexion2: join() ne prend qu'un argument 2015-04-10 16:16:36 +02:00
Daniel STAN
1726b669af menage: reset ip vieilles machines WiFi
On me le demande régulièrement, le voilà.
2015-04-10 13:05:02 +02:00
Gabriel Detraz
25022bde92 Ajout de la possibilité de perforer ses pages 2015-04-07 22:57:50 +02:00
Daniel STAN
66f3fd0707 derniere_connexion: catche EnvironmentError
Au moins, on ne crashera pas lamentablement au milieu de la boucle... Ce qui
permet d'être sûr qu'on màj tous les comptes nécessaires.
De toute façon, si on rencontre cette erreur sur un adh, c'est qu'un autre
script est en train de s'exécuter de manière concurrente.
2015-04-07 19:54:58 +02:00
Pierre-Elliott Bécue
ef7e52d892 On stocke l'heure du cron dans la signature, pour plus de clarté. 2015-04-07 00:32:56 +02:00
Pierre-Elliott Bécue
1b68e5f5fd On essaie de pas péter la compatibilité pour la création de home des clubs. 2015-04-06 19:28:34 +02:00
Pierre-Elliott Bécue
0c4a999b1f Les clubs aussi peuvent avoir un mail extérieur, techniquement. 2015-04-06 19:28:15 +02:00
Daniel STAN
ab2cf5b6e4 mail: ajoute fonction send_template
Pour tout faire en une seule commande
2015-04-05 14:43:28 +02:00
Daniel STAN
a742a6847c mail.py: add un peu de doc 2015-04-05 14:42:18 +02:00
Daniel STAN
ffb4a1d94f deconnexion2.py: modif ldap => context manager 2015-04-04 23:07:08 +02:00
Gabriel Detraz
4f7f75286c Un oubli de mode debug + espaces insécables 2015-04-04 22:53:56 +02:00
Pierre-Elliott Bécue
66d17ded88 La maintenance est effectuée par postgres, il est inutile de spécifier un user 2015-04-04 10:38:42 +02:00
Pierre-Elliott Bécue
f8f7c789a9 xact_start peut ne pas être renseigné si l'activité n'exécute aucune requête. 2015-04-04 10:38:01 +02:00
Daniel STAN
1d3b136bed freeradius: auth.py valide challenge chap 2015-04-03 23:57:35 +02:00
Daniel STAN
746e5db7b8 impression_hp: utilisation de lc_ldap 2015-04-03 21:03:38 +02:00
Pierre-Elliott Bécue
2993f31fd8 Were you footing on our gueule? 2015-04-03 15:16:13 +02:00
Pierre-Elliott Bécue
d9bd6a621d On met dans une transaction atomique le truncate/copy.
* Cela permet de s'assurer que la table contient bien des données en
 tout instant où elle le devrait (c'est-à-dire quand on ne la vide pas
 explicitement)
2015-04-03 14:20:56 +02:00
Gabriel Detraz
23c9df55eb Merge branch 'master' of https://gitlab.adm.crans.org/nounous/scripts
Pull de /usr/scripts et maj du depot
2015-04-01 23:35:27 +02:00
Gabriel Detraz
edfdd8ccc6 Correction de bugs et mise en prod . 2015-04-01 23:34:39 +02:00
Daniel STAN
d4cde74149 numeros_disponibles: ip_adm + boucle + propre 2015-03-31 13:18:05 +02:00
Pierre-Elliott Bécue
407c1e4799 Désormais, "quota2" est un peu plus propre. 2015-03-30 22:08:11 +02:00
Pierre-Elliott Bécue
5ef0534167 |name oublié 2015-03-30 20:39:04 +02:00
Pierre-Elliott Bécue
baa9855327 Passage à get_mail et |name pour le templating. 2015-03-30 20:36:15 +02:00
Pierre-Elliott Bécue
fdfdd23022 Revert des deux commits précédents : c'était un problème de types
* tuple vs list dans les results.
2015-03-30 17:18:54 +02:00
Pierre-Elliott Bécue
93ee03aee9 Et l'ordre des variables est important. 2015-03-30 17:08:10 +02:00
Pierre-Elliott Bécue
3041a8ee16 Quand on veut qu'une variable globale soit lisible dans une fonction, faut faire gaffe...
* ~100 mails sur une dizaine d'adhérents plus loin...
2015-03-30 17:05:50 +02:00
Pierre-Elliott Bécue
9c472f97c0 Coquille. 2015-03-30 09:07:19 +02:00
Pierre-Elliott Bécue
d6633b1258 On ne printe qu'en debug 2015-03-30 00:41:29 +02:00
Pierre-Elliott Bécue
38cf8eb8e9 Nouveau script de comptage d'upload et de déconnexion.
* Il compte en progressif et stocke les calculs temporaires
 dans une table appelée accounting
 * Aux itérations suivantes, il ne prend en compte que les deltas
2015-03-30 00:39:18 +02:00
Pierre-Elliott Bécue
6093c61b42 stamp_updated et non stamp_inserted 2015-03-30 00:38:46 +02:00
Pierre-Elliott Bécue
64bb5252ea On étale les requêtes SQL pour la lisibilité 2015-03-29 23:10:50 +02:00
Gabriel Detraz
83a1a611ef Correctif : affiche le login si le nom de pag diff 2015-03-29 01:51:29 +01:00
Gabriel Detraz
0410d42147 Macro pagesperso : .info->pgsql, via intranet 2015-03-27 21:08:05 +01:00
Gabriel Detraz
21e9851203 Mise de l'ancien quota.py aux archives 2015-03-27 21:04:15 +01:00
Pierre-Elliott Bécue
900d8db1a7 Quelques corrections mineures 2015-03-27 13:58:04 +01:00
Pierre-Elliott Bécue
54e7d79d44 On utilise get_mail pour récupérer le mail des adhérents. 2015-03-27 13:57:17 +01:00
Pierre-Elliott Bécue
6ec03b7e0e On améliore un peu les mails pour l'upload. 2015-03-27 13:56:09 +01:00
Gabriel Detraz
d184d948a7 Alertsms.py -> utils (emplacement provisoire) 2015-03-27 00:50:25 +01:00
Hamza Dely
1793b26ae2 Ajout du script pour le service alertsms 2015-03-27 00:09:19 +01:00
Daniel STAN
d31c24bcb9 mail: màj horaires perm
Merci Bernie pour l'info
2015-03-25 17:06:30 +01:00
Pierre-Elliott Bécue
842bbc145b La modif d'un club peut aussi générer des factures adh/connexion. 2015-03-25 14:49:18 +01:00
Pierre-Elliott Bécue
b2afd29bb9 Les frais ne sont plus facturés, et on ajoute une fonction de remise (for future work) 2015-03-25 14:48:45 +01:00
Pierre-Elliott Bécue
7046a8cc87 Mise à jour du mail, et correction d'une typo
* gestiom nom nom
2015-03-25 11:44:32 +01:00
Daniel STAN
002031b18d switchs2: s/sable/eap/ pour radius secondaire 2015-03-24 23:03:15 +01:00
Daniel STAN
d6b359e717 rids/nets: shrink bornes à /27 + ajout plage ipv6
Faudra valider en IN si on garde cette plage ou non ...
2015-03-23 20:19:22 +01:00
Daniel STAN
9824bb160a impression_hp: ColorModel=Grayscale
Pour compat avec le driver PCL
2015-03-23 20:07:28 +01:00
Daniel STAN
b04b8deda5 ridtools: find_rid_plage renvoie un couple 2015-03-23 19:19:52 +01:00
Daniel STAN
7bfa903078 freeradius: handler logging via radiusd module 2015-03-23 19:15:23 +01:00
Cron Daemon
e1e51b2240 [ethercodes.dat] Mise à jour du fichier vendeur 2015-03-23 01:46:34 +01:00
Gabriel Detraz
bbf4dcd8dd Semi-Récriture et nettoyage de quota.py 2015-03-22 23:59:46 +01:00
Pierre-Elliott Bécue
f31603f830 Import inutile de lc_ldap enlevé. 2015-03-22 02:22:44 +01:00
Pierre-Elliott Bécue
33eac6ffac Pylint était tout triste. 2015-03-22 02:15:44 +01:00
Pierre-Elliott Bécue
3f1b7401ca static_var n'a pas vraiment de raison d'être dans affichage.py 2015-03-22 02:12:27 +01:00
Gabriel Detraz
394531a537 quota.sh->usr/scripts,symlik depuis /usr/local/bin 2015-03-21 22:42:48 +01:00
Pierre-Elliott Bécue
e0cced3e4d On gère un peu mieux le message 2015-03-20 16:35:01 +01:00
Vincent Le Gallic
891fae63f6 [whosthere] Changement de path des xml des bornes 2015-03-16 19:34:18 +01:00
Cron Daemon
cdf2c98228 [ethercodes.dat] Mise à jour du fichier vendeur 2015-03-15 02:11:28 +01:00
Daniel STAN
b1ea6070bc vire ipset_allow (pour conn CROUS temporaire) 2015-03-12 21:24:06 +01:00
Daniel STAN
ae83dac934 auth.py: trompé d'1bit 2015-03-12 21:21:33 +01:00
Valentin Samir
ec603f1202 [firewall4] Komaz n'est plus routeur
Et du coup soyouz n'avait pas accès a zamok via adm (mac-ip) et
sa mailqueue grandissait.
Il y a du avoir des problèmes de communication wifi -> fixe only ou
fixe -> wifi-only avec les machines qui ont un pare-feu mais je ne l'ai pas
observé.
2015-03-12 13:03:25 +01:00
Pierre-Elliott Bécue
182ac4ad32 Update des attributs dans l'adhérent quand update des factures, et changement de sémantique. 2015-03-11 22:28:54 +01:00
Pierre-Elliott Bécue
86a054cbad *sifflotte* 2015-03-11 22:10:01 +01:00
Gabriel Detraz
ab17c9bdcd switch2.py template : snooping port trust -> int 2015-03-11 20:14:17 +01:00
Pierre-Elliott Bécue
3d5abd7fb3 On met une variable dans testing pour le debug de trigger. 2015-03-10 22:06:30 +01:00
Pierre-Elliott Bécue
b470c860c0 explicit.is.better.than.too.explicit.dude. 2015-03-10 21:51:08 +01:00
Pierre-Elliott Bécue
c0aa3c0e48 Oublié d'updater les arguments dans le cas d'une régénération explicite. 2015-03-10 21:14:16 +01:00
Pierre-Elliott Bécue
cfac2f811e Si l'arité des fonctions n'est pas compatible avec les arguments, on loggue, sans crasher. 2015-03-10 21:12:44 +01:00
Pierre-Elliott Bécue
4bc4cb7abe Readme à jour, et quelques modifications sur les noms de variables. 2015-03-10 21:06:16 +01:00
Pierre-Elliott Bécue
f228493399 Correctifs pour le commit précédent, et de quoi tester le chaînage. 2015-03-10 20:06:18 +01:00
Pierre-Elliott Bécue
201377528c Implémentation d'un gestionnaire d'événements sommaire. 2015-03-10 16:41:49 +01:00
Daniel STAN
6c97d6998f freeradius: ajoute les confs des sites & clients 2015-03-07 18:22:40 +01:00
Daniel STAN
14eaf53b19 ajout du plugin wifi_authorized sur fy 2015-03-07 11:49:17 +01:00
Pierre-Elliott Bécue
dd4887dad4 Ajout des horaires pour l'urne. 2015-03-07 01:24:43 +01:00
Pierre-Elliott Bécue
3a3788e19d Maj convocation AGO 2015-03-07 01:17:53 +01:00
Pierre-Elliott Bécue
e1d2c0935c Lors de la création des comptes, le setdefault sur self.modif était foireux. 2015-03-04 15:41:05 +01:00
Pierre-Elliott Bécue
6a26519418 Merdre, j'ai oublié un poing. 2015-03-03 14:59:37 +01:00
Pierre-Elliott Bécue
8ab3238a78 Il faut utiliser les shortnames pour les serveurs, dans bind. 2015-03-03 14:08:51 +01:00
Pierre-Elliott Bécue
64a59be2b7 Champs TXT correctement gérés et DROP des RR SPF (RFC 7208) 2015-03-03 14:01:51 +01:00
Pierre-Elliott Bécue
17461ccf7f Ajout des SPF aux DNS. 2015-03-03 13:54:43 +01:00
Pierre-Elliott Bécue
4b652b69b6 Commentaires et nettoyage dans bind. 2015-03-03 13:29:08 +01:00
Pierre-Elliott Bécue
15e83e1844 On fait en sorte que prise_etat gère mieux les prises virtuelles, pour que whos_lc arrête de planter. 2015-03-02 20:59:59 +01:00
Daniel STAN
cb3f063dc6 git-whatsnew : corrige le shabang 2015-03-02 20:31:42 +01:00
Pierre-Elliott Bécue
06bac151c4 batv-0.adm.crans.org n'est pas spécial 2015-03-02 20:05:38 +01:00
Gabriel Detraz
1d58966ebc statsVlans : répararation et dépoussiérage 2015-03-01 18:49:49 +01:00
Daniel STAN
8b551ae8c8 deconnexion: un template, ça mange de l'unicode 2015-03-01 16:42:21 +01:00
Daniel STAN
c8cdf3170e DisplayDict: évite les imports arbitraires 2015-02-27 12:30:33 +01:00
Daniel STAN
546cba760f auth.py: feeds nas password from secrets 2015-02-26 18:17:57 +01:00
Daniel STAN
ea4e86dbdd auth.py: enregistre mac *et/ou* rid 2015-02-26 18:16:13 +01:00
Daniel STAN
132636e7f9 freeradius: déplace tous les tests dans subdir 2015-02-26 18:15:03 +01:00
Daniel STAN
d36ceb7e3d auth.py: pas d'antisquattage dans le local d'étude 2015-02-26 09:21:29 +01:00
Gabriel Detraz
5d34a64653 Locate-wifi aux archives 2015-02-26 00:01:16 +01:00
Daniel STAN
27c3d1d842 wip: auth dynamique des clients (nas) radius 2015-02-25 23:21:29 +01:00
Gabriel Detraz
4f73302b13 recredite.py -> recredit_masse.py dans impression 2015-02-25 22:31:56 +01:00
Pierre-Elliott Bécue
635a37385d Autres scripts obsolètes. 2015-02-25 22:13:21 +01:00
Pierre-Elliott Bécue
8a4d7dfd78 Scripts obsolètes. 2015-02-25 22:07:02 +01:00
Gabriel Detraz
05f9c4580d ra2.py : on-off sur la prise quand bl ra 2015-02-25 20:41:41 +01:00
Gabriel Detraz
87e363e4bf chambre_on_off.py :réparé et importable maintenant 2015-02-25 19:33:40 +01:00
Pierre-Elliott Bécue
7034b74b25 Le smtp en starttls, c'est plus commun maintenant. 2015-02-25 17:44:28 +01:00
Daniel STAN
c0c36e50dc dhcpd: corrige omapi (my fault) 2015-02-24 16:39:55 +01:00
Gabriel Detraz
9f23c4a5d9 ra2.py : bug disconnect destinataire explicite 2015-02-23 19:25:14 +01:00
Gabriel Detraz
429da4655c surveillance : On met ra.py aux archives 2015-02-23 18:45:27 +01:00
Gabriel Detraz
7354b526c8 config.py : ajoute mac d'odlyd pour ramond(bcfg2) 2015-02-23 12:58:57 +01:00
Daniel STAN
d43ec0cfec testing.sh: introduit DBG_CLOGGER_PATH 2015-02-21 15:40:52 +01:00
Pierre-Elliott Bécue
5d9965af08 Une clef ssh est vieille tous les pi^2 ans. 2015-02-21 12:35:17 +01:00
Gabriel Detraz
8041a39397 surveillance/ra2.py : pose un bl ipv6_ra, et mail 2015-02-21 12:13:22 +01:00
Pierre-Elliott Bécue
1a333ad482 Un peu de modularité. 2015-02-21 11:42:29 +01:00
Pierre-Elliott Bécue
3897336b48 On n'exécute le code dans supprimer_compte que si l'adhérent en a bien un. 2015-02-20 20:57:28 +01:00
Daniel STAN
f3711404a6 gest_crans: s'adapte à dialog (plus de '\n') (bis)
J'en avais oublié
2015-02-20 18:16:48 +01:00
Daniel STAN
5c3a8ee252 annuaires_pg: retire switchs spéciaux en double 2015-02-20 18:06:50 +01:00
Pierre-Elliott Bécue
01911d4edf LDIRPATH set à /home/becue/temp ? Hmmm… 2015-02-20 17:41:59 +01:00
Pierre-Elliott Bécue
cb361615c4 Le dossier de stockage des logs est une variable globale
* Ça facilitera le passage en environnement de test
2015-02-20 17:26:41 +01:00
Pierre-Elliott Bécue
36aaa0dab9 Améliorations sur la gestion des sorties. 2015-02-20 17:11:52 +01:00
Pierre-Elliott Bécue
786eaca90e CLogger utilise un formatter custom et est timezone-aware maintenant. 2015-02-20 11:07:39 +01:00
Pierre-Elliott Bécue
48573c47d4 Ajout de batv-0 aux switches ignorés par all_switchs. 2015-02-19 11:12:46 +01:00
Pierre-Elliott Bécue
0411b3d986 del_user prend une liste de chaînes en argument. 2015-02-19 00:39:55 +01:00
Pierre-Elliott Bécue
2ffb7ca2f7 On ajoute le bâtiment v dans les configs. 2015-02-18 20:43:33 +01:00
Pierre-Elliott Bécue
da71e80be1 Correctifs sur le précédent commit, et prise en compte dans mkhome. 2015-02-18 20:43:10 +01:00
Pierre-Elliott Bécue
962d9df0f3 Prise en charge de l'attribut mailExterieur comme redirection. 2015-02-18 19:58:33 +01:00
Gabriel Detraz
60d7087c60 [wiki/macro pad] Les espaces sont biens maintenant 2015-02-18 16:39:15 +01:00
Pierre-Elliott Bécue
46d571dd70 On permute dans la recherche les champs pour clubs et pour machines. 2015-02-18 16:37:03 +01:00
Daniel STAN
41e97d7cf5 [radius/wifi] bl isolement + no tagging => reject 2015-02-18 16:15:16 +01:00
Daniel STAN
09a9e291c0 màj fichier vendeur (ethercodes.dat) 2015-02-17 20:07:45 +01:00
Daniel STAN
251d90f1c3 testing.sh: ajoute DBG_WIFIMAP_DB 2015-02-17 20:02:02 +01:00
Daniel STAN
d39ce7f224 python.sh: ajoute commentaires explicatifs 2015-02-17 20:01:49 +01:00
Daniel STAN
2af454a72b gest_crans: s'adapte à dialog (plus de '\n')
Désormais dialog semble renvoyer des listes de chaînes déjà strippées.
2015-02-17 12:20:35 +01:00
Daniel STAN
33fe1b91db gest_crans_lc: grammar 2015-02-16 15:04:22 +01:00
Daniel STAN
64daf7afe1 gest_crans_lc: fonction adh-related in adherent.py 2015-02-16 15:03:34 +01:00
Daniel STAN
7838057814 [ldap/gest_crans] revert "unicode sandwich"
Pas encore stable.
2015-02-16 14:58:32 +01:00
338 changed files with 33374 additions and 24601 deletions

11
.gitignore vendored
View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,683 @@
"""All Python Type client support for Bcfg2."""
__revision__ = '$Revision$'
import binascii
from datetime import datetime
import difflib
import errno
import grp
import logging
import os
import pwd
import shutil
import stat
import sys
import time
# py3k compatibility
if sys.hexversion >= 0x03000000:
unicode = str
import Bcfg2.Client.Tools
import Bcfg2.Options
from Bcfg2.Client import XML
log = logging.getLogger('python')
# map between dev_type attribute and stat constants
device_map = {'block': stat.S_IFBLK,
'char': stat.S_IFCHR,
'fifo': stat.S_IFIFO}
def calcPerms(initial, perms):
"""This compares ondisk permissions with specified ones."""
pdisp = [{1:stat.S_ISVTX, 2:stat.S_ISGID, 4:stat.S_ISUID},
{1:stat.S_IXUSR, 2:stat.S_IWUSR, 4:stat.S_IRUSR},
{1:stat.S_IXGRP, 2:stat.S_IWGRP, 4:stat.S_IRGRP},
{1:stat.S_IXOTH, 2:stat.S_IWOTH, 4:stat.S_IROTH}]
tempperms = initial
if len(perms) == 3:
perms = '0%s' % (perms)
pdigits = [int(perms[digit]) for digit in range(4)]
for index in range(4):
for (num, perm) in list(pdisp[index].items()):
if pdigits[index] & num:
tempperms |= perm
return tempperms
def normGid(entry):
"""
This takes a group name or gid and
returns the corresponding gid or False.
"""
try:
try:
return int(entry.get('group'))
except:
return int(grp.getgrnam(entry.get('group'))[2])
except (OSError, KeyError):
log.error('GID normalization failed for %s. Does group %s exist?'
% (entry.get('name'), entry.get('group')))
return False
def normUid(entry):
"""
This takes a user name or uid and
returns the corresponding uid or False.
"""
try:
try:
return int(entry.get('owner'))
except:
return int(pwd.getpwnam(entry.get('owner'))[2])
except (OSError, KeyError):
log.error('UID normalization failed for %s. Does owner %s exist?'
% (entry.get('name'), entry.get('owner')))
return False
def isString(strng, encoding):
"""
Returns true if the string contains no ASCII control characters
and can be decoded from the specified encoding.
"""
for char in strng:
if ord(char) < 9 or ord(char) > 13 and ord(char) < 32:
return False
try:
strng.decode(encoding)
return True
except:
return False
class Python(Bcfg2.Client.Tools.Tool):
"""Python File support code."""
name = 'Python'
__handles__ = [('Python', 'file'),
('Python', None)]
__req__ = {'Python': ['name']}
# grab paranoid options from /etc/bcfg2.conf
opts = {'ppath': Bcfg2.Options.PARANOID_PATH,
'max_copies': Bcfg2.Options.PARANOID_MAX_COPIES}
setup = Bcfg2.Options.OptionParser(opts)
setup.parse([])
ppath = setup['ppath']
max_copies = setup['max_copies']
def canInstall(self, entry):
"""Check if entry is complete for installation."""
if Bcfg2.Client.Tools.Tool.canInstall(self, entry):
if (entry.tag,
entry.get('type'),
entry.text,
entry.get('empty', 'false')) == ('Python',
'file',
None,
'false'):
return False
return True
else:
return False
def gatherCurrentData(self, entry):
if entry.tag == 'Python' and entry.get('type') == 'file':
try:
ondisk = os.stat(entry.get('name'))
except OSError:
entry.set('current_exists', 'false')
self.logger.debug("%s %s does not exist" %
(entry.tag, entry.get('name')))
return False
try:
entry.set('current_owner', str(ondisk[stat.ST_UID]))
entry.set('current_group', str(ondisk[stat.ST_GID]))
except (OSError, KeyError):
pass
entry.set('perms', str(oct(ondisk[stat.ST_MODE])[-4:]))
def Verifydirectory(self, entry, modlist):
"""Verify Path type='directory' entry."""
if entry.get('perms') == None or \
entry.get('owner') == None or \
entry.get('group') == None:
self.logger.error('Entry %s not completely specified. '
'Try running bcfg2-lint.' % (entry.get('name')))
return False
while len(entry.get('perms', '')) < 4:
entry.set('perms', '0' + entry.get('perms', ''))
try:
ondisk = os.stat(entry.get('name'))
except OSError:
entry.set('current_exists', 'false')
self.logger.debug("%s %s does not exist" %
(entry.tag, entry.get('name')))
return False
try:
owner = str(ondisk[stat.ST_UID])
group = str(ondisk[stat.ST_GID])
except (OSError, KeyError):
self.logger.error('User/Group resolution failed for path %s' % \
entry.get('name'))
owner = 'root'
group = '0'
finfo = os.stat(entry.get('name'))
perms = oct(finfo[stat.ST_MODE])[-4:]
if entry.get('mtime', '-1') != '-1':
mtime = str(finfo[stat.ST_MTIME])
else:
mtime = '-1'
pTrue = ((owner == str(normUid(entry))) and
(group == str(normGid(entry))) and
(perms == entry.get('perms')) and
(mtime == entry.get('mtime', '-1')))
pruneTrue = True
ex_ents = []
if entry.get('prune', 'false') == 'true' \
and (entry.tag == 'Path' and entry.get('type') == 'directory'):
# check for any extra entries when prune='true' attribute is set
try:
entries = ['/'.join([entry.get('name'), ent]) \
for ent in os.listdir(entry.get('name'))]
ex_ents = [e for e in entries if e not in modlist]
if ex_ents:
pruneTrue = False
self.logger.debug("Directory %s contains extra entries:" % \
entry.get('name'))
self.logger.debug(ex_ents)
nqtext = entry.get('qtext', '') + '\n'
nqtext += "Directory %s contains extra entries:" % \
entry.get('name')
nqtext += ":".join(ex_ents)
entry.set('qtest', nqtext)
[entry.append(XML.Element('Prune', path=x)) \
for x in ex_ents]
except OSError:
ex_ents = []
pruneTrue = True
if not pTrue:
if owner != str(normUid(entry)):
entry.set('current_owner', owner)
self.logger.debug("%s %s ownership wrong" % \
(entry.tag, entry.get('name')))
nqtext = entry.get('qtext', '') + '\n'
nqtext += "%s owner wrong. is %s should be %s" % \
(entry.get('name'), owner, entry.get('owner'))
entry.set('qtext', nqtext)
if group != str(normGid(entry)):
entry.set('current_group', group)
self.logger.debug("%s %s group wrong" % \
(entry.tag, entry.get('name')))
nqtext = entry.get('qtext', '') + '\n'
nqtext += "%s group is %s should be %s" % \
(entry.get('name'), group, entry.get('group'))
entry.set('qtext', nqtext)
if perms != entry.get('perms'):
entry.set('current_perms', perms)
self.logger.debug("%s %s permissions are %s should be %s" %
(entry.tag,
entry.get('name'),
perms,
entry.get('perms')))
nqtext = entry.get('qtext', '') + '\n'
nqtext += "%s %s perms are %s should be %s" % \
(entry.tag,
entry.get('name'),
perms,
entry.get('perms'))
entry.set('qtext', nqtext)
if mtime != entry.get('mtime', '-1'):
entry.set('current_mtime', mtime)
self.logger.debug("%s %s mtime is %s should be %s" \
% (entry.tag, entry.get('name'), mtime,
entry.get('mtime')))
nqtext = entry.get('qtext', '') + '\n'
nqtext += "%s mtime is %s should be %s" % \
(entry.get('name'), mtime, entry.get('mtime'))
entry.set('qtext', nqtext)
if entry.get('type') != 'file':
nnqtext = entry.get('qtext')
nnqtext += '\nInstall %s %s: (y/N) ' % (entry.get('type'),
entry.get('name'))
entry.set('qtext', nnqtext)
return pTrue and pruneTrue
def Installdirectory(self, entry):
"""Install Path type='directory' entry."""
if entry.get('perms') == None or \
entry.get('owner') == None or \
entry.get('group') == None:
self.logger.error('Entry %s not completely specified. '
'Try running bcfg2-lint.' % \
(entry.get('name')))
return False
self.logger.info("Installing directory %s" % (entry.get('name')))
try:
fmode = os.lstat(entry.get('name'))
if not stat.S_ISDIR(fmode[stat.ST_MODE]):
self.logger.debug("Found a non-directory entry at %s" % \
(entry.get('name')))
try:
os.unlink(entry.get('name'))
exists = False
except OSError:
self.logger.info("Failed to unlink %s" % \
(entry.get('name')))
return False
else:
self.logger.debug("Found a pre-existing directory at %s" % \
(entry.get('name')))
exists = True
except OSError:
# stat failed
exists = False
if not exists:
parent = "/".join(entry.get('name').split('/')[:-1])
if parent:
try:
os.stat(parent)
except:
self.logger.debug('Creating parent path for directory %s' % (entry.get('name')))
for idx in range(len(parent.split('/')[:-1])):
current = '/'+'/'.join(parent.split('/')[1:2+idx])
try:
sloc = os.stat(current)
except OSError:
try:
os.mkdir(current)
continue
except OSError:
return False
if not stat.S_ISDIR(sloc[stat.ST_MODE]):
try:
os.unlink(current)
os.mkdir(current)
except OSError:
return False
try:
os.mkdir(entry.get('name'))
except OSError:
self.logger.error('Failed to create directory %s' % \
(entry.get('name')))
return False
if entry.get('prune', 'false') == 'true' and entry.get("qtest"):
for pent in entry.findall('Prune'):
pname = pent.get('path')
ulfailed = False
if os.path.isdir(pname):
self.logger.info("Not removing extra directory %s, "
"please check and remove manually" % pname)
continue
try:
self.logger.debug("Unlinking file %s" % pname)
os.unlink(pname)
except OSError:
self.logger.error("Failed to unlink path %s" % pname)
ulfailed = True
if ulfailed:
return False
return self.Installpermissions(entry)
def Verifyfile(self, entry, _):
"""Verify Python type='file' entry."""
# permissions check + content check
permissionStatus = self.Verifydirectory(entry, _)
tbin = False
if entry.text == None and entry.get('empty', 'false') == 'false':
self.logger.error("Cannot verify incomplete Python type='%s' %s" %
(entry.get('type'), entry.get('name')))
return False
if entry.get('encoding', 'ascii') == 'base64':
tempdata = binascii.a2b_base64(entry.text)
tbin = True
elif entry.get('empty', 'false') == 'true':
tempdata = ''
else:
tempdata = entry.text
if type(tempdata) == unicode:
try:
tempdata = tempdata.encode(self.setup['encoding'])
except UnicodeEncodeError:
e = sys.exc_info()[1]
self.logger.error("Error encoding file %s:\n %s" % \
(entry.get('name'), e))
different = False
content = None
if not os.path.exists(entry.get("name")):
# first, see if the target file exists at all; if not,
# they're clearly different
different = True
content = ""
else:
# next, see if the size of the target file is different
# from the size of the desired content
try:
estat = os.stat(entry.get('name'))
except OSError:
err = sys.exc_info()[1]
self.logger.error("Failed to stat %s: %s" %
(err.filename, err))
return False
if len(tempdata) != estat[stat.ST_SIZE]:
different = True
else:
# finally, read in the target file and compare them
# directly. comparison could be done with a checksum,
# which might be faster for big binary files, but
# slower for everything else
try:
content = open(entry.get('name')).read()
except IOError:
err = sys.exc_info()[1]
self.logger.error("Failed to read %s: %s" %
(err.filename, err))
return False
different = content != tempdata
if different:
if self.setup['interactive']:
prompt = [entry.get('qtext', '')]
if not tbin and content is None:
# it's possible that we figured out the files are
# different without reading in the local file. if
# the supplied version of the file is not binary,
# we now have to read in the local file to figure
# out if _it_ is binary, and either include that
# fact or the diff in our prompts for -I
try:
content = open(entry.get('name')).read()
except IOError:
err = sys.exc_info()[1]
self.logger.error("Failed to read %s: %s" %
(err.filename, err))
return False
if tbin or not isString(content, self.setup['encoding']):
# don't compute diffs if the file is binary
prompt.append('Binary file, no printable diff')
else:
diff = self._diff(content, tempdata,
difflib.unified_diff,
filename=entry.get("name"))
if diff:
udiff = '\n'.join(diff)
try:
prompt.append(udiff.decode(self.setup['encoding']))
except UnicodeDecodeError:
prompt.append("Binary file, no printable diff")
else:
prompt.append("Diff took too long to compute, no "
"printable diff")
prompt.append("Install %s %s: (y/N): " % (entry.tag,
entry.get('name')))
entry.set("qtext", "\n".join(prompt))
if entry.get('sensitive', 'false').lower() != 'true':
if content is None:
# it's possible that we figured out the files are
# different without reading in the local file. we
# now have to read in the local file to figure out
# if _it_ is binary, and either include the whole
# file or the diff for reports
try:
content = open(entry.get('name')).read()
except IOError:
err = sys.exc_info()[1]
self.logger.error("Failed to read %s: %s" %
(err.filename, err))
return False
if tbin or not isString(content, self.setup['encoding']):
# don't compute diffs if the file is binary
entry.set('current_bfile', binascii.b2a_base64(content))
else:
diff = self._diff(content, tempdata, difflib.ndiff,
filename=entry.get("name"))
if diff:
entry.set("current_bdiff",
binascii.b2a_base64("\n".join(diff)))
elif not tbin and isString(content, self.setup['encoding']):
entry.set('current_bfile', binascii.b2a_base64(content))
elif permissionStatus == False and self.setup['interactive']:
prompt = [entry.get('qtext', '')]
prompt.append("Install %s %s: (y/N): " % (entry.tag,
entry.get('name')))
entry.set("qtext", "\n".join(prompt))
return permissionStatus and not different
def Installfile(self, entry):
"""Install Python type='file' entry."""
self.logger.info("Installing file %s" % (entry.get('name')))
parent = "/".join(entry.get('name').split('/')[:-1])
if parent:
try:
os.stat(parent)
except:
self.logger.debug('Creating parent path for config file %s' % \
(entry.get('name')))
current = '/'
for next in parent.split('/')[1:]:
current += next + '/'
try:
sloc = os.stat(current)
try:
if not stat.S_ISDIR(sloc[stat.ST_MODE]):
self.logger.debug('%s is not a directory; recreating' \
% (current))
os.unlink(current)
os.mkdir(current)
except OSError:
return False
except OSError:
try:
self.logger.debug("Creating non-existent path %s" % current)
os.mkdir(current)
except OSError:
return False
# If we get here, then the parent directory should exist
if (entry.get("paranoid", False) in ['true', 'True']) and \
self.setup.get("paranoid", False) and not \
(entry.get('current_exists', 'true') == 'false'):
bkupnam = entry.get('name').replace('/', '_')
# current list of backups for this file
try:
bkuplist = [f for f in os.listdir(self.ppath) if
f.startswith(bkupnam)]
except OSError:
e = sys.exc_info()[1]
self.logger.error("Failed to create backup list in %s: %s" %
(self.ppath, e.strerror))
return False
bkuplist.sort()
while len(bkuplist) >= int(self.max_copies):
# remove the oldest backup available
oldest = bkuplist.pop(0)
self.logger.info("Removing %s" % oldest)
try:
os.remove("%s/%s" % (self.ppath, oldest))
except:
self.logger.error("Failed to remove %s/%s" % \
(self.ppath, oldest))
return False
try:
# backup existing file
shutil.copy(entry.get('name'),
"%s/%s_%s" % (self.ppath, bkupnam,
datetime.isoformat(datetime.now())))
self.logger.info("Backup of %s saved to %s" %
(entry.get('name'), self.ppath))
except IOError:
e = sys.exc_info()[1]
self.logger.error("Failed to create backup file for %s" % \
(entry.get('name')))
self.logger.error(e)
return False
try:
newfile = open("%s.new"%(entry.get('name')), 'w')
if entry.get('encoding', 'ascii') == 'base64':
filedata = binascii.a2b_base64(entry.text)
elif entry.get('empty', 'false') == 'true':
filedata = ''
else:
if type(entry.text) == unicode:
filedata = entry.text.encode(self.setup['encoding'])
else:
filedata = entry.text
newfile.write(filedata)
newfile.close()
try:
os.chown(newfile.name, normUid(entry), normGid(entry))
except KeyError:
self.logger.error("Failed to chown %s to %s:%s" %
(newfile.name, entry.get('owner'),
entry.get('group')))
os.chown(newfile.name, 0, 0)
except OSError:
err = sys.exc_info()[1]
self.logger.error("Could not chown %s: %s" % (newfile.name,
err))
os.chmod(newfile.name, calcPerms(stat.S_IFREG, entry.get('perms')))
os.rename(newfile.name, entry.get('name'))
if entry.get('mtime', '-1') != '-1':
try:
os.utime(entry.get('name'), (int(entry.get('mtime')),
int(entry.get('mtime'))))
except:
self.logger.error("File %s mtime fix failed" \
% (entry.get('name')))
return False
return True
except (OSError, IOError):
err = sys.exc_info()[1]
if err.errno == errno.EACCES:
self.logger.info("Failed to open %s for writing" % (entry.get('name')))
else:
print(err)
return False
def Verifypermissions(self, entry, _):
"""Verify Path type='permissions' entry"""
if entry.get('perms') == None or \
entry.get('owner') == None or \
entry.get('group') == None:
self.logger.error('Entry %s not completely specified. '
'Try running bcfg2-lint.' % (entry.get('name')))
return False
if entry.get('recursive') in ['True', 'true']:
# verify ownership information recursively
owner = normUid(entry)
group = normGid(entry)
for root, dirs, files in os.walk(entry.get('name')):
for p in dirs + files:
path = os.path.join(root, p)
pstat = os.stat(path)
if owner != pstat.st_uid:
# owner mismatch for path
entry.set('current_owner', str(pstat.st_uid))
self.logger.debug("%s %s ownership wrong" % \
(entry.tag, path))
nqtext = entry.get('qtext', '') + '\n'
nqtext += ("Owner for path %s is incorrect. "
"Current owner is %s but should be %s\n" % \
(path, pstat.st_uid, entry.get('owner')))
nqtext += ("\nInstall %s %s: (y/N): " %
(entry.tag, entry.get('name')))
entry.set('qtext', nqtext)
return False
if group != pstat.st_gid:
# group mismatch for path
entry.set('current_group', str(pstat.st_gid))
self.logger.debug("%s %s group wrong" % \
(entry.tag, path))
nqtext = entry.get('qtext', '') + '\n'
nqtext += ("Group for path %s is incorrect. "
"Current group is %s but should be %s\n" % \
(path, pstat.st_gid, entry.get('group')))
nqtext += ("\nInstall %s %s: (y/N): " %
(entry.tag, entry.get('name')))
entry.set('qtext', nqtext)
return False
return self.Verifydirectory(entry, _)
def _diff(self, content1, content2, difffunc, filename=None):
rv = []
start = time.time()
longtime = False
for diffline in difffunc(content1.split('\n'),
content2.split('\n')):
now = time.time()
rv.append(diffline)
if now - start > 5 and not longtime:
if filename:
self.logger.info("Diff of %s taking a long time" %
filename)
else:
self.logger.info("Diff taking a long time")
longtime = True
elif now - start > 30:
if filename:
self.logger.error("Diff of %s took too long; giving up" %
filename)
else:
self.logger.error("Diff took too long; giving up")
return False
return rv
def Installpermissions(self, entry):
"""Install POSIX permissions"""
if entry.get('perms') == None or \
entry.get('owner') == None or \
entry.get('group') == None:
self.logger.error('Entry %s not completely specified. '
'Try running bcfg2-lint.' % (entry.get('name')))
return False
plist = [entry.get('name')]
if entry.get('recursive') in ['True', 'true']:
# verify ownership information recursively
owner = normUid(entry)
group = normGid(entry)
for root, dirs, files in os.walk(entry.get('name')):
for p in dirs + files:
path = os.path.join(root, p)
pstat = os.stat(path)
if owner != pstat.st_uid or group != pstat.st_gid:
# owner mismatch for path
plist.append(path)
try:
for p in plist:
os.chown(p, normUid(entry), normGid(entry))
os.chmod(p, calcPerms(stat.S_IFDIR, entry.get('perms')))
return True
except (OSError, KeyError):
self.logger.error('Permission fixup failed for %s' % \
(entry.get('name')))
return False
def InstallNone(self, entry):
return self.Installfile(entry)
def VerifyNone(self, entry, _):
return self.Verifyfile(entry, _)
def InstallPython(self, entry):
"""Dispatch install to the proper method according to type"""
ret = getattr(self, 'Install%s' % entry.get('type'))
return ret(entry)
def VerifyPython(self, entry, _):
"""Dispatch verify to the proper method according to type"""
ret = getattr(self, 'Verify%s' % entry.get('type'))
return ret(entry, _)

View file

@ -11,6 +11,8 @@ Licence : GPLv2
import sys import sys
from config import NETs from config import NETs
from iptools import AddrInNet from iptools import AddrInNet
import netaddr
try: try:
from dialog import Dialog from dialog import Dialog
except ImportError: except ImportError:
@ -18,14 +20,6 @@ except ImportError:
repertoire = '/usr/scripts/var/numeros_disponibles/' repertoire = '/usr/scripts/var/numeros_disponibles/'
"""
Un petit hack de rien du tout pour s'assurer qu'on n'attribue
pas ces adresses. Certains services risquent de continuer
d'essayer de se connecter a ces adresses
"""
ancien_vlan_adm = ['10.231.136.0/24']
def lister_ip_dispo(plage): def lister_ip_dispo(plage):
f = open(repertoire + 'ip_' + plage) f = open(repertoire + 'ip_' + plage)
lignes = f.readlines() lignes = f.readlines()
@ -48,25 +42,14 @@ def update_ip(plage, occupees):
net = NETs[plage] net = NETs[plage]
pool_ip = [] # Pool d'IP à tester pool_ip = [] # Pool d'IP à tester
for ne in net: for ne in net:
ip = ne.split('/')[0] ne = netaddr.IPNetwork(ne)
ip = ip.split('.') for ip in ne:
n = [] # avoid .255 and .0 (even for non-/24 nets)
for i in ip: if (ip.value & 255) in [0,255]:
n.append(int(i)) continue
while 1: pool_ip.append(str(ip))
if n[3] < 254:
n[3] += 1
else:
n[2] += 1
n[3] = 1
if n[2] == 255: break
ip = "%d.%d.%d.%d" % tuple(n)
if not AddrInNet(ip, ne):
# On est allé trop loin
break
pool_ip.append(ip)
resultat = ''.join('%s\n' % ip for ip in pool_ip if ip not in occupees and not AddrInNet(ip, ancien_vlan_adm)) resultat = ''.join('%s\n' % ip for ip in pool_ip if ip not in occupees)
f = open(repertoire + 'ip_' + plage,'w') f = open(repertoire + 'ip_' + plage,'w')
f.write(resultat) f.write(resultat)
@ -78,6 +61,17 @@ def update_ip_fixe(occupees):
def update_ip_wifi_adh(occupees): def update_ip_wifi_adh(occupees):
update_ip('wifi-adh','ip_wifi-adh', occupees) update_ip('wifi-adh','ip_wifi-adh', occupees)
TO_COMPUTE = [
'wifi',
'wifi-adh',
'serveurs',
'adherents',
'bornes',
'adm',
'personnel-ens',
'fil',
]
if __name__ == "__main__": if __name__ == "__main__":
if "--cron" in sys.argv: if "--cron" in sys.argv:
cron = True cron = True
@ -88,9 +82,9 @@ if __name__ == "__main__":
dlg.gauge_start(text="Recherche des machines...", backtitle="numeros_disponibles") dlg.gauge_start(text="Recherche des machines...", backtitle="numeros_disponibles")
ip_occupees = lister_ip_utilisees() ip_occupees = lister_ip_utilisees()
done = 1 done = 1
for net in NETs.keys(): for net in TO_COMPUTE:
if not cron: if not cron:
dlg.gauge_update(int(done*100/(len(NETs)+1)), text="IP libres dans %s" % net, update_text=True) dlg.gauge_update(int(done*100/(len(TO_COMPUTE)+1)), text="IP libres dans %s" % net, update_text=True)
update_ip(net, ip_occupees) update_ip(net, ip_occupees)
done += 1 done += 1
if not cron: if not cron:

View file

@ -85,7 +85,7 @@ def liste_2b(warn_mail=[]):
# Utilisateurs connectés sur vo sur place # Utilisateurs connectés sur vo sur place
if gethostname() == 'vo': if gethostname() == 'vo':
cprint('---=== W(ho) sur vo ===---', 'bleu') cprint('---=== W(ho) sur vo ===---', 'bleu')
ttyfound = system("/usr/bin/w -s | grep tty`fgconsole`") ttyfound = system("/usr/bin/w -s | grep ?xdm?")
print '' print ''
# Conclusion # Conclusion

View file

@ -1,4 +1,4 @@
#! /usr/bin/env python #!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# ############################################################# # #############################################################
# .. # ..
@ -38,7 +38,8 @@ import commands
import shutil import shutil
import syslog import syslog
import stat 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 from cranslib.deprecated import module as deprecated_module
deprecated_module() deprecated_module()
from cranslib.utils import QuoteForPOSIX as escapeForShell from cranslib.utils import QuoteForPOSIX as escapeForShell

View file

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

View file

@ -97,8 +97,8 @@ FROM (
FROM FROM
upload upload
WHERE WHERE
stamp_inserted > now() - interval '1 day' stamp_updated > now() - interval '1 day'
AND stamp_inserted < now() AND stamp_updated < now()
AND NOT (ip_dst <<= inet%(plage_ens)s OR ip_dst <<= inet%(plage_ipv6)s OR ip_dst <<= inet%(appt)s OR ip_src <<= inet%(ipv6_local)s OR ip_src=inet'0.0.0.0' OR ip_src <<= inet%(plage_adm)s OR ip_dst <<= inet%(plage_adm)s) AND NOT (ip_dst <<= inet%(plage_ens)s OR ip_dst <<= inet%(plage_ipv6)s OR ip_dst <<= inet%(appt)s OR ip_src <<= inet%(ipv6_local)s OR ip_src=inet'0.0.0.0' OR ip_src <<= inet%(plage_adm)s OR ip_dst <<= inet%(plage_adm)s)
AND (ip_src <<= inet%(allone)s OR ip_src <<= inet%(alltwo)s OR ip_src <<= inet%(plage_ipv6)s OR ip_src <<= inet%(appt)s) AND (ip_src <<= inet%(allone)s OR ip_src <<= inet%(alltwo)s OR ip_src <<= inet%(plage_ipv6)s OR ip_src <<= inet%(appt)s)
AND NOT EXISTS AND NOT EXISTS
@ -199,10 +199,10 @@ for elupload, eltype, elid in uploadeurs:
data = {'dn': theid, data = {'dn': theid,
'blid': len(proprio.blacklist())} 'blid': len(proprio.blacklist())}
reco_url = mail_module.validation_url('upload', data) reco_url = mail_module.validation_url('upload', data)
reco_url_error = "" reco_url_error = u""
except Exception as e: except Exception as e:
reco_url_error = "[[erreur de génération: %r]]" % e reco_url_error = u"[[erreur de génération: %r]]" % e
reco_url = "" reco_url = u""
mail_data = { mail_data = {
'from': upload.expediteur, 'from': upload.expediteur,

50
archive/utils/quota.py Normal file
View file

@ -0,0 +1,50 @@
# -*- coding: utf8 -*-
import os
LABELS = {
"/home":u"Dossier personnel",
"/var/mail":u"Boite de réception"
}
def getFloat( chose ):
chose = chose.replace(',', '.')
return float(chose)
def getUserQuota( userLogin ):
pipe = os.popen("sudo quota %s" % userLogin)
string_result = pipe.read()
pipe.close()
string_result = string_result.split("\n")
quotas = []
for a_line in string_result[2:-1]:
usage, quota, limite, percentage, fs = a_line.split("\t")
line_dict = {
"label": "Quota personnel",
"usage":getFloat(usage),
"quota":getFloat(quota),
"limite":getFloat(limite),
"%":getFloat(percentage),
"filesystem":fs, # pourquoi pas ?
}
quotas.append(line_dict)
return quotas
def fake_getUserQuota( userLogin ):
return [
{'%': 33.9,
'quota': 390.62,
'label': u'Dossier personnel (fake)',
'limite': 585.94,
'filesystem': '/home',
'usage': 420.32},
{'%': 0.1,
'quota': 100.00,
'label': u'Boite de r\xe9ception (fake)',
'limite': 150.00,
'filesystem': '/var/mail',
'usage': 0.06}
]

View file

@ -0,0 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Contient les valeurs par défaut du plugin python
de Bcfg2"""
DEFAULT_USER = 'root'
DEFAULT_GROUP = 'root'
DEFAULT_ACLS = 0644
INCLUDES = "../etc/python"

View file

@ -0,0 +1,113 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
"""SafeEnvironment implementation for use of exec"""
import os
import cStringIO
import PythonDefaults
import PythonFile
class SafeEnvironment(dict):
"""Environnement isolé dans lequel on exécute un script"""
def __init__(self, additionnal=None, parent=None):
# Création de l'environment initial
super(self.__class__, self).__init__({
# Écrit: variable keysep tostring(value)
"defvar": self.defvar,
# La convertion en chaîne de charactère
"tostring": self.tostring,
# Définition des convertions
"conv": {
bool: {
True: "yes",
False: "no",
},
list: lambda l: ", ".join([
str(x)
for x in l
]),
tuple: lambda l: ", ".join([
str(x)
for x in l
]),
},
# Fonction de base pour imprimer quelque chose
"out": self.out,
"_out": self._out,
# Le séparateur pour la forme: variable keysep valeur
"keysep": "=",
# Le charactère de commentaire
"comment_start": "#",
# Du mapping de certaines fonctions
"include": self.include,
# Du mapping de certaines fonctions
"dump": self.dump,
# Infos standard pour le fichier (écrasable localement)
"info": {
'owner': PythonDefaults.DEFAULT_USER,
'group': PythonDefaults.DEFAULT_GROUP,
'mode': PythonDefaults.DEFAULT_ACLS,
}
})
if additionnal is None:
additionnal = {}
super(self.__class__, self).update(additionnal)
# On crée le flux dans lequel le fichier de config sera généré
self.stream = cStringIO.StringIO()
# Le Pythonfile parent est référencé ici
self.parent = parent
# Les trucs inclus
self.included = []
def __setitem__(self, variable, value):
"""Lorsqu'on définit une variable, si elle est listée dans la variable
exports, on l'incorpore dans le fichier produit"""
super(self.__class__, self).__setitem__(variable, value)
def defvar(self, variable, value):
"""Quand on fait un export, on utilise defvar pour incorporer la variable
et sa valeur dans le fichier produit"""
# On écrit mavariable = toto, en appliquant une éventuelle conversion à toto
self.out("%s%s%s" % (variable, self['keysep'], self.tostring(value)))
def out(self, string=""):
"""C'est le print local. Sauf qu'on écrit dans self.stream"""
self._out("%s\n" % (string,))
def _out(self, string=""):
"""C'est le print local sans retour à la ligne."""
self.stream.write(string)
def tostring(self, value):
"""On convertit un objet python dans un format "string" sympa.
En vrai c'est horrible et il faudrait virer ce genre de kludge."""
convertor = self["conv"].get(type(value))
if convertor:
if type(convertor) == dict:
return convertor[value]
else:
return convertor(value)
else:
return str(value)
def dump(self, incfile):
"""On exécute le fichier python dans l'environnement courant
incfile est le nom du fichier, sans le .py"""
filename = os.path.join(self.parent.parent.include, "%s.py" % (incfile,))
python_file = PythonFile.PythonFile(filename, self.parent.parent)
python_file.run(environment=self)
def include(self, incfile):
"""Pareil qu'au dessus, mais on ne le fait que si ça n'a pas
été fait"""
if incfile in self.included:
return
self.included.append(incfile)
self.dump(incfile)

View file

@ -0,0 +1,33 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
"""Ce module est prévu pour héberger des factories, stockant toute
instance d'un fichier Python déjà compilé."""
class PythonFileFactory(object):
"""Cette Factory stocke l'ensemble des fichiers Python déjà instanciés.
Elle garantit entre autre leur unicité dans le fonctionnement du plugin"""
#: Stocke la liste des instances avec leur chemin absolu.
files = {}
@classmethod
def get(cls, path):
"""Récupère l'instance si elle existe, ou renvoit None"""
return cls.files.get(path, None)
@classmethod
def record(cls, path, instance):
"""Enregistre l'instance dans la Factory"""
cls.files[path] = instance
@classmethod
def flush_one(cls, path):
"""Vire une instance du dico"""
instance_to_delete = cls.files.pop(path, None)
del instance_to_delete
@classmethod
def flush(cls):
"""Vire toutes les instances du dico"""
for path in cls.files.keys():
cls.flush_one(path)

View file

@ -0,0 +1,221 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
"""Fournit une couche d'abstraction Python pour les fichiers du même
nom"""
import os
import sys
import re
import marshal
import cStringIO
from Bcfg2.Server.Plugin import Debuggable
from .PythonFactories import PythonFileFactory
import PythonEnv
import PythonTools
__RE_SPECIAL_LINE = re.compile(r"^([ \t]*)(@|%)(.*)$", re.MULTILINE)
__RE_AFFECTATION = re.compile(r"([a-zA-Z_][a-zA-Z_0-9]*)[ \t]*=")
__RE_SPACE_SEP = re.compile(r"([^ \t]*)[ \t]+=?(.*)")
class PythonFile(Debuggable):
"""Classe représentant un fichier Python"""
#: Permet de savoir si l'instance a déjà été initialisée
initialized = False
def __new__(cls, path, parent=None):
"""Si le fichier a déjà été enregistré dans la Factory, on
le retourne, et on évite de réinstancier la classe.
path est le chemin absolu du fichier"""
path = os.path.normpath(path)
file_instance = PythonFileFactory.get(path)
if file_instance is None:
file_instance = super(PythonFile, cls).__new__(cls)
PythonFileFactory.record(path, file_instance)
return file_instance
def __init__(self, path, parent=None):
"""Initialisation, si non déjà faite"""
if self.initialized:
return
super(self.__class__, self).__init__()
#: A string containing the raw data in this file
self.data = None
#: Le chemin complet du fichier
self.path = os.path.normpath(path)
#: Le nom du fichier
self.name = os.path.basename(self.path)
#: Un logger
self.logger = PythonTools.LOGGER
#: Le plugin parent est pointé pour des raisons pratiques
self.parent = parent
#: C'est bon, c'est initialisé
self.initialized = True
def exists(self):
"""Teste l'existence du fichier"""
return os.path.exists(self.path)
def HandleEvent(self, event=None):
""" HandleEvent is called whenever the FAM registers an event.
:param event: The event object
:type event: Bcfg2.Server.FileMonitor.Event
:returns: None
"""
if event and event.code2str() not in ['exists', 'changed', 'created']:
return
try:
self.load()
except IOError:
err = sys.exc_info()[1]
self.logger.error("Failed to read file %s: %s" % (self.name, err))
except:
err = sys.exc_info()[1]
self.logger.error("Failed to parse file %s: %s" % (self.name, err))
def __repr__(self):
return "%s: %s" % (self.__class__.__name__, self.name)
def load(self, refresh=True):
"""Charge le fichier"""
if self.data is not None and not refresh:
return
try:
directory = os.path.dirname(self.path)
compiled_file = os.path.join(directory, ".%s.COMPILED" % (self.name,))
if os.path.exists(compiled_file) and os.stat(self.path).st_mtime <= os.stat(compiled_file).st_mtime:
self.data = marshal.load(open(compiled_file, 'r'))
else:
self.data = compileSource(open(self.path, 'r').read(), self.path, self.logger)
cfile = open(compiled_file, "w")
marshal.dump(self.data, cfile)
cfile.close()
except Exception as error:
PythonTools.log_traceback(self.path, 'compilation', error, self.logger)
def run(self, additionnal=None, environment=None):
"""Exécute le code"""
if self.data is None:
self.load(True)
if additionnal is None:
additionnal = {}
if environment is None:
environment = PythonEnv.SafeEnvironment(additionnal, self)
# Lors de l'exécution d'un fichier, on inclut
# toujours common (ie on l'exécute dans l'environnement)
environment.include("common")
try:
exec(self.data, environment)
except Exception:
sys.stderr.write('code: %r\n' % (self.data,))
raise
return environment.stream.getvalue(), environment['info']
#+---------------------------------------------+
#| Tools for compilation |
#+---------------------------------------------+
def compileSource(source, filename="", logger=None):
'''Compile un script'''
# On commence par remplacer les lignes de la forme
# @xxx par out("xxx")
newsource = cStringIO.StringIO()
start = 0
# Parsing de goret : on boucle sur les lignes spéciales,
# c'est-à-dire celles commençant par un @ ou un % précédé
# par d'éventuelles espaces/tabs.
for match in __RE_SPECIAL_LINE.finditer(source):
# On prend tout ce qui ne nous intéresse pas et on l'ajoute.
newsource.write(source[start:match.start()])
# On redéfinit start.
start = match.end()
# On écrit le premier groupe (les espaces et cie)
newsource.write(match.group(1))
# Le linetype est soit @ soit %
linetype = match.group(2)
# @ c'est du print.
if linetype == "@":
# On prend ce qui nous intéresse, et on fait quelques remplacements
# pour éviter les plantages.
line = match.group(3).replace("\\", "\\\\").replace('"', '\\"')
# Si la ligne est un commentaire, on la reproduit en remplaçant éventuellement
# le # par le bon caractère.
if line and line[0] == "#":
newsource.write('out(comment_start + "')
line = line[1:]
# Sinon bah....
else:
newsource.write('out("')
# On écrit ladite ligne
newsource.write(line)
# Et un superbe \n.
newsource.write('")')
# %, affectation.
elif linetype == "%":
# On récupère le reste.
line = match.group(3)
# On fait du matching clef/valeur
match = __RE_AFFECTATION.match(line)
if match:
# Le nom est le premier groupe.
# Et après c'est weird...
varname = match.group(1)
newsource.write(line)
newsource.write("; defvar('")
newsource.write(varname)
newsource.write("', tostring(")
newsource.write(varname)
newsource.write("))\n")
else:
# Pareil, sauf que cette fois, ce qu'on fait a un sens.
match = __RE_SPACE_SEP.match(line)
newsource.write("defvar('")
newsource.write(match.group(1))
# Le tostring est facultatif.
newsource.write("', tostring(")
newsource.write(match.group(2))
newsource.write("))\n")
# On continue.
newsource.write(source[start:])
if logger:
try:
logger.info(newsource.getvalue())
except:
print "Le logger de BCFG2 c'est de la merde, il refuse le non ascii."
print "Voici ce que j'ai essayé de logguer."
print newsource.getvalue()
return compile(newsource.getvalue(), filename, "exec")

View file

@ -0,0 +1,294 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# PythonPlugin.py
# ---------
#
# Copyright © 2015 Pierre-Elliott Bécue <becue@crans.org>
"""Plugin servant à gérer des fichiers python, dont la sortie sera
la configuration d'un client."""
#: N'exporte que la classe Python
__all__ = [
"Python",
]
import os
import re
import binascii
from Bcfg2.Server.Plugin import Plugin, Generator, PluginExecutionError, track_statistics
from Bcfg2.Server.Plugin.base import Debuggable
import PythonTools
import PythonDefaults
import PythonFile
class Python(Plugin, Generator, Debuggable):
"""Générateur offrant des fonctionnalités de templating pour les fichiers python"""
name = 'Python'
#: Les DirectoryBacked ont des fonctions de monitoring
#: intégrées. Quand des changements arrivent sur les dossiers,
#: c'est la merde, il est préférable de relancer Bcfg2, car
#: FileMonitor ne sait pas démonitorer/remonitorer.
#: En revanche, pour les fichiers, il appelle __child__ comme
#: "générateur" pour les trucs à surveiller. Quand un fichier
#: est créé/modifié, sa méthode HandleEvent est appelée.
__child__ = PythonFile.PythonFile
#: Ce module gère plein de choses.
patterns = re.compile(r'.*')
#: Ignore ces chemins spécifiques
ignore = re.compile(r'.*\.(COMPILED|pyc)')
__version__ = '2.0'
__author__ = 'becue@crans.org'
def __init__(self, core, datastore):
"""Pour initialiser le plugin"""
#: Initialise un certain nombre de choses en background
Plugin.__init__(self, core, datastore)
Debuggable.__init__(self)
#: self.entries contains information about the files monitored
#: by this object. The keys of the dict are the relative
#: paths to the files. The values are the objects (of type
#: :attr:`__child__`) that handle their contents.
self.entries = {}
#: self.handles contains information about the directories
#: monitored by this object. The keys of the dict are the
#: values returned by the initial fam.AddMonitor() call (which
#: appear to be integers). The values are the relative paths of
#: the directories.
self.handles = {}
#: FileMonitor
self.fam = self.core.fam
#: Monitor everything in the plugin's directory
if not os.path.exists(self.data):
self.logger.warning("%s does not exist, creating" % (self.data,))
os.makedirs(self.data)
self.add_directory_monitor('')
#: Dossier des includes
self.include = os.path.abspath(os.path.join(self.data, PythonDefaults.INCLUDES))
#: Quand on initialise un DirectoryBacked, on a déjà un monitoring de
#: self.data, donc on a besoin que des includes
self.add_directory_monitor(PythonDefaults.INCLUDES)
@track_statistics()
def HandlesEntry(self, entry, metadata):
"""Vérifie si l'entrée est gérée par le plugin"""
relpath = entry.get('name')[1:]
if relpath in self.entries:
return True
return False
@track_statistics()
def HandleEntry(self, entry, metadata):
"""Construit le fichier demandé"""
# On récupère le code qui va bien.
relpath = entry.get('name')[1:]
python_file = self.entries[relpath]
# Et le nom de fichier.
fname = entry.get('realname', entry.get('name'))
# Si on est en débug, on loggue ce qu'on fait.
PythonTools.debug("Building config file: %s" % (fname,), PythonTools.LOGGER, 'blue')
# On crée un environnement autonome pour exécuter le fichier.
additionnal = {
'metadata': metadata,
}
try:
text, info = python_file.run(additionnal)
except Exception as error:
PythonTools.log_traceback(fname, 'exec', error, PythonTools.LOGGER)
raise PluginExecutionError
# On récupère les infos
if info.get('encoding', '') == 'base64':
text = binascii.b2a_base64(text)
# lxml n'accepte que de l'ascii ou de l'unicode
# donc faut décoder.
try:
entry.text = text.decode("UTF-8")
except:
# solution de fallback
entry.text = text.decode("ISO-8859-15")
# En cas de débug, on stocke les données
PythonTools.debug(entry.text, PythonTools.LOGGER)
# On récupère les permissions depuis le dico "info".
# En théorie, les valeurs par défaut ne devraient pas être utilisées
# Car elles sont déjà affectées dans Pygen
entry.attrib['owner'] = info.get('owner', PythonDefaults.DEFAULT_USER)
entry.attrib['group'] = info.get('group', PythonDefaults.DEFAULT_GROUP)
entry.attrib['mode'] = oct(info.get('mode', PythonDefaults.DEFAULT_ACLS))
if 'encoding' in info:
entry.attrib['encoding'] = info['encoding']
def add_directory_monitor(self, relative):
""" Add a new directory to the FAM for monitoring.
:param relative: Path name to monitor. This must be relative
to the plugin's directory. An empty string
value ("") will cause the plugin directory
itself to be monitored.
:type relative: string
:returns: None
"""
#: On normalise pour éviter des problèmes quand le FileMonitor
#: voit des changements par la suite.
#: Les chemins sont absolus pour la même raison.
dirpathname = os.path.normpath(os.path.join(self.data, relative))
if relative not in self.handles.values():
if not os.path.isdir(dirpathname):
self.logger.error("%s is not a directory" % (dirpathname,))
return
#: reqid est un chemin absolu sans trailing slash
reqid = self.fam.AddMonitor(dirpathname, self)
self.handles[reqid] = relative
def add_entry(self, relative, event):
""" Add a new file to our tracked entries, and to our FAM for
monitoring.
:param relative: Path name to monitor. This must be relative
to the plugin's directory.
:type relative: string:
:param event: FAM event that caused this entry to be added.
:type event: Bcfg2.Server.FileMonitor.Event
:returns: None
"""
#: Les entrées sont en relatif depuis le dossier de config
self.entries[relative] = self.__child__(
os.path.join(self.data, relative),
self
)
self.entries[relative].HandleEvent(event)
def HandleEvent(self, event):
""" Handle FAM events.
This method is invoked by the FAM when it detects a change to
a filesystem object we have requsted to be monitored.
This method manages the lifecycle of events related to the
monitored objects, adding them to our list of entries and
creating objects of type :attr:`__child__` that actually do
the domain-specific processing. When appropriate, it
propogates events those objects by invoking their HandleEvent
method in turn.
:param event: FAM event that caused this entry to be added.
:type event: Bcfg2.Server.FileMonitor.Event
:returns: None
"""
action = event.code2str()
# Exclude events for actions we don't care about
if action == 'endExist':
return
if event.requestID not in self.handles:
self.logger.warn(
"Got %s event with unknown handle (%s) for %s" % (action, event.requestID, event.filename)
)
return
# Clean up path names
event.filename = os.path.normpath(event.filename)
if event.filename.startswith(self.data) or os.path.normpath(event.requestID) == event.filename:
# the first event we get is on the data directory itself
event.filename = event.filename[len(os.path.normpath(event.requestID)) + 1:]
if self.ignore and self.ignore.search(event.filename):
self.logger.debug("Ignoring event %s" % (event.filename,))
return
# Calculate the absolute and relative paths this event refers to
abspath = os.path.join(self.data, self.handles[event.requestID],
event.filename)
relpath = os.path.join(self.handles[event.requestID],
event.filename).lstrip('/')
if action == 'deleted':
for key in list(self.entries.keys()):
if key.startswith(relpath):
del self.entries[key]
# We remove values from self.entries, but not
# self.handles, because the FileMonitor doesn't stop
# watching a directory just because it gets deleted. If it
# is recreated, we will start getting notifications for it
# again without having to add a new monitor.
elif os.path.isdir(abspath):
# Deal with events for directories
if action in ['exists', 'created']:
self.add_directory_monitor(relpath)
elif action == 'changed':
if relpath in self.entries:
# Ownerships, permissions or timestamps changed on
# the directory. None of these should affect the
# contents of the files, though it could change
# our ability to access them.
#
# It seems like the right thing to do is to cancel
# monitoring the directory and then begin
# monitoring it again. But the current FileMonitor
# class doesn't support canceling, so at least let
# the user know that a restart might be a good
# idea.
self.logger.warn(
"Directory properties for %s changed, please consider restarting the server" % (abspath)
)
else:
# Got a "changed" event for a directory that we
# didn't know about. Go ahead and treat it like a
# "created" event, but log a warning, because this
# is unexpected.
self.logger.warn(
"Got %s event for unexpected dir %s" % (action, abspath)
)
self.add_directory_monitor(relpath)
else:
self.logger.warn(
"Got unknown dir event %s %s %s" % (event.requestID, event.code2str(), abspath)
)
elif self.patterns.search(event.filename):
if action in ['exists', 'created']:
self.add_entry(relpath, event)
elif action == 'changed':
if relpath in self.entries:
self.entries[relpath].HandleEvent(event)
else:
# Got a "changed" event for a file that we didn't
# know about. Go ahead and treat it like a
# "created" event, but log a warning, because this
# is unexpected.
self.logger.warn(
"Got %s event for unexpected file %s" % (action, abspath)
)
self.add_entry(relpath, event)
else:
self.logger.warn(
"Got unknown file event %s %s %s" % (event.requestID, event.code2str(), abspath)
)
else:
self.logger.warn(
"Could not process filename %s; ignoring" % (event.filename)
)

View file

@ -0,0 +1,73 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Fournit quelques outils pour le plugin Python"""
import os
import logging
import cStringIO
import traceback
LOGGER = logging.getLogger('Bcfg2.Plugins.Python')
COLOR_CODE = {
'grey': 30,
'red': 31,
'green': 32,
'yellow': 33,
'blue': 34,
'purple': 35,
'cyan': 36,
}
BCFG2_DEBUG = os.getenv("BCFG2_DEBUG")
BCFG2_DEBUG_COLOR = os.getenv("BCFG2_DEBUG_COLOR")
def debug(message, logger, color=None):
"""Stocke dans un logger les messages de debug"""
if not BCFG2_DEBUG:
return
if BCFG2_DEBUG_COLOR and color:
logger.info("\033[1;%dm%s\033[0m" % (COLOR_CODE[color], message))
else:
logger.info(message)
def log_traceback(fname, section, exn, logger):
"""En cas de traceback, on le loggue sans faire planter
le serveur bcfg2"""
logger.error('Python %s error: %s: %s: %s' % (section, fname, str(exn.__class__).split('.', 2)[1], str(exn)))
stream = cStringIO.StringIO()
traceback.print_exc(file=stream)
for line in stream.getvalue().splitlines():
logger.error('Python %s error: -> %s' % (section, line))
class PythonIncludePaths(object):
"""C'est un objet qui stocke les dossier d'inclusion python"""
includes = []
@classmethod
def get(cls, index, default):
"""Retourne includes[index] ou default"""
if len(cls.includes) > index:
return cls.includes[index]
return default
@classmethod
def append(cls, value):
"""Ajoute une valeur à la liste"""
cls.includes.append(value)
@classmethod
def remove(cls, value):
"""Retire une valeur à la liste"""
if value in cls.includes:
cls.includes.remove(value)
@classmethod
def pop(cls, index):
"""Vire un index si existant"""
if len(cls.includes) > index:
return cls.includes.pop(index)

View file

@ -0,0 +1,6 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
"""Python plugin initializator for
Bcfg2"""
from .PythonPlugin import Python

View file

@ -29,21 +29,21 @@ device_map = {'block': stat.S_IFBLK,
'fifo': stat.S_IFIFO} 'fifo': stat.S_IFIFO}
def calcPerms(initial, perms): def calcMode(initial, mode):
"""This compares ondisk permissions with specified ones.""" """This compares ondisk permissions with specified ones."""
pdisp = [{1:stat.S_ISVTX, 2:stat.S_ISGID, 4:stat.S_ISUID}, pdisp = [{1:stat.S_ISVTX, 2:stat.S_ISGID, 4:stat.S_ISUID},
{1:stat.S_IXUSR, 2:stat.S_IWUSR, 4:stat.S_IRUSR}, {1:stat.S_IXUSR, 2:stat.S_IWUSR, 4:stat.S_IRUSR},
{1:stat.S_IXGRP, 2:stat.S_IWGRP, 4:stat.S_IRGRP}, {1:stat.S_IXGRP, 2:stat.S_IWGRP, 4:stat.S_IRGRP},
{1:stat.S_IXOTH, 2:stat.S_IWOTH, 4:stat.S_IROTH}] {1:stat.S_IXOTH, 2:stat.S_IWOTH, 4:stat.S_IROTH}]
tempperms = initial tempmode = initial
if len(perms) == 3: if len(mode) == 3:
perms = '0%s' % (perms) mode = '0%s' % (mode)
pdigits = [int(perms[digit]) for digit in range(4)] pdigits = [int(mode[digit]) for digit in range(4)]
for index in range(4): for index in range(4):
for (num, perm) in list(pdisp[index].items()): for (num, perm) in list(pdisp[index].items()):
if pdigits[index] & num: if pdigits[index] & num:
tempperms |= perm tempmode |= perm
return tempperms return tempmode
def normGid(entry): def normGid(entry):
@ -137,18 +137,18 @@ class Python(Bcfg2.Client.Tools.Tool):
entry.set('current_group', str(ondisk[stat.ST_GID])) entry.set('current_group', str(ondisk[stat.ST_GID]))
except (OSError, KeyError): except (OSError, KeyError):
pass pass
entry.set('perms', str(oct(ondisk[stat.ST_MODE])[-4:])) entry.set('mode', str(oct(ondisk[stat.ST_MODE])[-4:]))
def Verifydirectory(self, entry, modlist): def Verifydirectory(self, entry, modlist):
"""Verify Path type='directory' entry.""" """Verify Path type='directory' entry."""
if entry.get('perms') == None or \ if entry.get('mode') == None or \
entry.get('owner') == None or \ entry.get('owner') == None or \
entry.get('group') == None: entry.get('group') == None:
self.logger.error('Entry %s not completely specified. ' self.logger.error('Entry %s not completely specified. '
'Try running bcfg2-lint.' % (entry.get('name'))) 'Try running bcfg2-lint.' % (entry.get('name')))
return False return False
while len(entry.get('perms', '')) < 4: while len(entry.get('mode', '')) < 4:
entry.set('perms', '0' + entry.get('perms', '')) entry.set('mode', '0' + entry.get('mode', ''))
try: try:
ondisk = os.stat(entry.get('name')) ondisk = os.stat(entry.get('name'))
except OSError: except OSError:
@ -165,14 +165,14 @@ class Python(Bcfg2.Client.Tools.Tool):
owner = 'root' owner = 'root'
group = '0' group = '0'
finfo = os.stat(entry.get('name')) finfo = os.stat(entry.get('name'))
perms = oct(finfo[stat.ST_MODE])[-4:] mode = oct(finfo[stat.ST_MODE])[-4:]
if entry.get('mtime', '-1') != '-1': if entry.get('mtime', '-1') != '-1':
mtime = str(finfo[stat.ST_MTIME]) mtime = str(finfo[stat.ST_MTIME])
else: else:
mtime = '-1' mtime = '-1'
pTrue = ((owner == str(normUid(entry))) and pTrue = ((owner == str(normUid(entry))) and
(group == str(normGid(entry))) and (group == str(normGid(entry))) and
(perms == entry.get('perms')) and (mode == entry.get('mode')) and
(mtime == entry.get('mtime', '-1'))) (mtime == entry.get('mtime', '-1')))
pruneTrue = True pruneTrue = True
@ -217,19 +217,19 @@ class Python(Bcfg2.Client.Tools.Tool):
nqtext += "%s group is %s should be %s" % \ nqtext += "%s group is %s should be %s" % \
(entry.get('name'), group, entry.get('group')) (entry.get('name'), group, entry.get('group'))
entry.set('qtext', nqtext) entry.set('qtext', nqtext)
if perms != entry.get('perms'): if mode != entry.get('mode'):
entry.set('current_perms', perms) entry.set('current_mode', mode)
self.logger.debug("%s %s permissions are %s should be %s" % self.logger.debug("%s %s permissions are %s should be %s" %
(entry.tag, (entry.tag,
entry.get('name'), entry.get('name'),
perms, mode,
entry.get('perms'))) entry.get('mode')))
nqtext = entry.get('qtext', '') + '\n' nqtext = entry.get('qtext', '') + '\n'
nqtext += "%s %s perms are %s should be %s" % \ nqtext += "%s %s mode are %s should be %s" % \
(entry.tag, (entry.tag,
entry.get('name'), entry.get('name'),
perms, mode,
entry.get('perms')) entry.get('mode'))
entry.set('qtext', nqtext) entry.set('qtext', nqtext)
if mtime != entry.get('mtime', '-1'): if mtime != entry.get('mtime', '-1'):
entry.set('current_mtime', mtime) entry.set('current_mtime', mtime)
@ -249,7 +249,7 @@ class Python(Bcfg2.Client.Tools.Tool):
def Installdirectory(self, entry): def Installdirectory(self, entry):
"""Install Path type='directory' entry.""" """Install Path type='directory' entry."""
if entry.get('perms') == None or \ if entry.get('mode') == None or \
entry.get('owner') == None or \ entry.get('owner') == None or \
entry.get('group') == None: entry.get('group') == None:
self.logger.error('Entry %s not completely specified. ' self.logger.error('Entry %s not completely specified. '
@ -547,7 +547,7 @@ class Python(Bcfg2.Client.Tools.Tool):
err = sys.exc_info()[1] err = sys.exc_info()[1]
self.logger.error("Could not chown %s: %s" % (newfile.name, self.logger.error("Could not chown %s: %s" % (newfile.name,
err)) err))
os.chmod(newfile.name, calcPerms(stat.S_IFREG, entry.get('perms'))) os.chmod(newfile.name, calcMode(stat.S_IFREG, entry.get('mode')))
os.rename(newfile.name, entry.get('name')) os.rename(newfile.name, entry.get('name'))
if entry.get('mtime', '-1') != '-1': if entry.get('mtime', '-1') != '-1':
try: try:
@ -568,7 +568,7 @@ class Python(Bcfg2.Client.Tools.Tool):
def Verifypermissions(self, entry, _): def Verifypermissions(self, entry, _):
"""Verify Path type='permissions' entry""" """Verify Path type='permissions' entry"""
if entry.get('perms') == None or \ if entry.get('mode') == None or \
entry.get('owner') == None or \ entry.get('owner') == None or \
entry.get('group') == None: entry.get('group') == None:
self.logger.error('Entry %s not completely specified. ' self.logger.error('Entry %s not completely specified. '
@ -637,7 +637,7 @@ class Python(Bcfg2.Client.Tools.Tool):
def Installpermissions(self, entry): def Installpermissions(self, entry):
"""Install POSIX permissions""" """Install POSIX permissions"""
if entry.get('perms') == None or \ if entry.get('mode') == None or \
entry.get('owner') == None or \ entry.get('owner') == None or \
entry.get('group') == None: entry.get('group') == None:
self.logger.error('Entry %s not completely specified. ' self.logger.error('Entry %s not completely specified. '
@ -659,7 +659,7 @@ class Python(Bcfg2.Client.Tools.Tool):
try: try:
for p in plist: for p in plist:
os.chown(p, normUid(entry), normGid(entry)) os.chown(p, normUid(entry), normGid(entry))
os.chmod(p, calcPerms(stat.S_IFDIR, entry.get('perms'))) os.chmod(p, calcMode(stat.S_IFDIR, entry.get('mode')))
return True return True
except (OSError, KeyError): except (OSError, KeyError):
self.logger.error('Permission fixup failed for %s' % \ self.logger.error('Permission fixup failed for %s' % \

View file

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

16
bin/all/quota Executable file
View file

@ -0,0 +1,16 @@
#!/bin/bash
if [[ $1 = "" ]] || [[ $1 = $USER ]] ; then
/usr/bin/quota
else
/usr/bin/quota $*
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 != "") {
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
fs = ""
}
}'

View file

@ -6,38 +6,117 @@
# License : GPLv3 # License : GPLv3
# Date : 27/04/2014 # Date : 27/04/2014
import os
import datetime
import pytz
import logging import logging
TZ = pytz.timezone('Europe/Paris')
LDIRPATH = os.getenv('DBG_CLOGGER_PATH', '/var/log/clogger')
class CLogger(logging.Logger): class CLogger(logging.Logger):
""" """
Crans logger Crans logger.
""" """
def __init__(self, loggerName, service, level, debug=False): def __init__(self, loggerName, service=None, level="info", debug=False):
""" """
Initializes logger. The debug variable is useful to have a print to stdout (when debugging) Initializes logger. The debug variable is useful to have a print to stdout (when debugging)
""" """
super(CLogger, self).__init__(loggerName) super(CLogger, self).__init__(loggerName)
# Creates FileHandler self.c_formatter = None
self.fh = logging.FileHandler("/var/log/clogger/%s.log" % (loggerName,)) self.c_file_handler = None
self.c_sh = None
self.c_level = level
# Catches appropriate level in logging. # When no service is specified, we don't put the reference in the format.
self.fhlevel = getattr(logging, level.upper(), logging.INFO) if service is None:
self.fh.setLevel(self.fhlevel) self.c_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
else:
self.c_format = "%%(asctime)s - %%(name)s - %(service)s - %%(levelname)s - %%(message)s" % {'service': service}
self.create_formatter()
self.apply_file_handler(loggerName)
if debug:
self.apply_stream_handler()
def get_file_handler_path(self):
"""Returns the file handler path"""
if self.__file_handler_path is None:
return ''
return self.__file_handler_path
def create_formatter(self):
"""Creates a formatter based on CFormatter class.
It uses self.format as a source."""
if self.c_formatter is not None:
return
# Creates formatter # Creates formatter
self.formatter = logging.Formatter('%%(asctime)s - %%(name)s - %(service)s - %%(levelname)s - %%(message)s' % {'service': service}) self.c_formatter = CFormatter(self.c_format, "%Y-%m-%dT%H:%M:%S.%f%z")
def apply_stream_handler(self):
"""Creates a streamhandler that prints to stdout.
Its level is debug"""
self.c_sh = logging.StreamHandler()
self.c_shlevel = logging.DEBUG
self.c_sh.setLevel(self.c_shlevel)
self.c_sh.setFormatter(self.c_formatter)
self.addHandler(self.c_sh)
def apply_file_handler(self, loggerName):
"""Creates a file handler which level is given by self.c_level"""
if self.c_file_handler is not None:
return
# Computes the file handler name using service name.
self.__file_handler_path = os.path.join(LDIRPATH, "%s.log" % (loggerName,))
# Creates FileHandler
self.c_file_handler = logging.FileHandler(self.__file_handler_path)
# Catches appropriate level in logging.
self.c_file_handler_level = getattr(logging, self.c_level.upper(), logging.INFO)
self.c_file_handler.setLevel(self.c_file_handler_level)
# Adds formatter to FileHandler # Adds formatter to FileHandler
self.fh.setFormatter(self.formatter) self.c_file_handler.setFormatter(self.c_formatter)
if debug:
self.sh = logging.StreamHandler()
self.shlevel = logging.DEBUG
self.sh.setLevel(self.shlevel)
self.sh.setFormatter(self.formatter)
self.addHandler(self.sh)
# Adds FileHandler to Handlers # Adds FileHandler to Handlers
self.addHandler(self.fh) self.addHandler(self.c_file_handler)
class CFormatter(logging.Formatter):
"""
This Formatter subclasses the classic formatter to provide a
timezone-aware logging.
"""
converter = datetime.datetime.fromtimestamp
def formatTime(self, record, datefmt=None):
"""
Return the creation time of the specified LogRecord as formatted text.
This method should be called from format() by a formatter which
wants to make use of a formatted time. This method can be overridden
in formatters to provide for any specific requirement, but the
basic behaviour is as follows: if datefmt (a string) is specified,
it is used with time.strftime() to format the creation time of the
record. Otherwise, the ISO8601 format is used. The resulting
string is returned. This function uses a user-configurable function
to convert the creation time to a tuple. By default, time.localtime()
is used; to change this for a particular formatter instance, set the
'converter' attribute to a function with the same signature as
time.localtime() or time.gmtime(). To change it for all formatters,
for example if you want all logging times to be shown in GMT,
set the 'converter' attribute in the Formatter class.
"""
ct = self.converter(record.created, TZ)
ct = ct.replace(microsecond=int(record.msecs * 1000))
if datefmt:
s = ct.strftime(datefmt)
else:
s = ct.strftime("%Y-%m-%d %H:%M:%S.%f")
return s

20
cranslib/decorators.py Normal file
View file

@ -0,0 +1,20 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import functools
def static_var(*couples):
"""Decorator setting static variable
to a function.
"""
# Using setattr magic, we set static
# variable on function. This avoid
# computing stuff again.
def decorate(fun):
functools.wraps(fun)
for (name, val) in couples:
setattr(fun, name, val)
return fun
return decorate

View file

@ -1,15 +1,22 @@
# ⁻*- coding: utf-8 -*- # ⁻*- coding: utf-8 -*-
# """
# Ce fichier contient la définition de plusieurs fonctions d'interface à freeradius Backend python pour freeradius.
# qui peuvent être appelées (suivant les configurations) à certains moment de
# l'éxécution. Ce fichier contient la définition de plusieurs fonctions d'interface à
# freeradius qui peuvent être appelées (suivant les configurations) à certains
moment de l'authentification, en WiFi, filaire, ou par les NAS eux-mêmes.
Inspirés d'autres exemples trouvés ici :
https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_python/
"""
import logging import logging
import netaddr import netaddr
import radiusd # Module magique freeradius (radiusd.py is dummy) import radiusd # Module magique freeradius (radiusd.py is dummy)
import ldap import ldap
import os import os
import binascii
import hashlib
import lc_ldap.shortcuts import lc_ldap.shortcuts
from lc_ldap.crans_utils import escape as escape_ldap from lc_ldap.crans_utils import escape as escape_ldap
@ -18,40 +25,63 @@ import lc_ldap.objets
import gestion.config.config as config import gestion.config.config as config
from gestion.gen_confs.trigger import trigger_generate_cochon as trigger_generate from gestion.gen_confs.trigger import trigger_generate_cochon as trigger_generate
import annuaires_pg import annuaires_pg
from gestion import secrets_new as secrets
#: Serveur radius de test (pas la prod)
TEST_SERVER = bool(os.getenv('DBG_FREERADIUS', False)) TEST_SERVER = bool(os.getenv('DBG_FREERADIUS', False))
#: Le taggage dynamique de vlan (dans la réponse) est désactivé sur WiFi
WIFI_DYN_VLAN = TEST_SERVER
#: Suffixe à retirer du username si présent (en wifi)
USERNAME_SUFFIX_WIFI = '.wifi.crans.org' USERNAME_SUFFIX_WIFI = '.wifi.crans.org'
#: Suffixe à retirer du username si présent (filaire)
USERNAME_SUFFIX_FIL = '.crans.org' USERNAME_SUFFIX_FIL = '.crans.org'
## -*- Logging -*- ## -*- Logging -*-
# Initialisation d'un logger pour faire des stats etc
# pour l'instant, on centralise tout sur thot en mode debug class RadiusdHandler(logging.Handler):
"""Handler de logs pour freeradius"""
def emit(self, record):
"""Process un message de log, en convertissant les niveaux"""
if record.levelno >= logging.WARN:
rad_sig = radiusd.L_ERR
elif record.levelno >= logging.INFO:
rad_sig = radiusd.L_INFO
else:
rad_sig = radiusd.L_DBG
radiusd.radlog(rad_sig, record.msg)
# Initialisation d'un logger (pour logguer unifié)
logger = logging.getLogger('auth.py') logger = logging.getLogger('auth.py')
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(name)s: [%(levelname)s] %(message)s') formatter = logging.Formatter('%(name)s: [%(levelname)s] %(message)s')
handler = logging.handlers.SysLogHandler(address = '/dev/log') handler = RadiusdHandler()
try: handler.setFormatter(formatter)
handler.addFormatter(formatter)
except AttributeError:
handler.formatter = formatter
logger.addHandler(handler) logger.addHandler(handler)
## -*- Types de blacklists -*- ## -*- Types de blacklists -*-
#: reject tout de suite #: reject tout de suite
bl_reject = [u'bloq'] BL_REJECT = [u'bloq']
#: place sur le vlan isolement #: place sur le vlan isolement
bl_isolement = [u'virus', u'autodisc_virus', u'autodisc_p2p', u'ipv6_ra'] BL_ISOLEMENT = [u'virus', u'autodisc_virus', u'autodisc_p2p', u'ipv6_ra']
# TODO carte_etudiant: dépend si sursis ou non (regarder lc_ldap)
# TODO LOGSSSSS
#: place sur accueil #: place sur accueil
bl_accueil = [] BL_ACCUEIL = [u'paiement']
# Ces blacklists ont des effets soft (portail captif port 80) # À classer:
#bl_accueil = [u'carte_etudiant', u'chambre_invalide', u'paiement'] # [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:
# personne n'y habite ! ( G091 -> G097: salle d'étude du rdc du G)
PUBLIC_CHBRE = ['G091', 'G092', 'G093', 'G094', 'G095', 'G096', 'G097']
## -*- Decorateurs -*- ## -*- Decorateurs -*-
# À appliquer sur les fonctions qui ont besoin d'une conn ldap # À appliquer sur les fonctions qui ont besoin d'une conn ldap
@ -60,7 +90,7 @@ use_ldap_admin = lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5,
use_ldap = lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5, use_ldap = lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5,
constructor=lc_ldap.shortcuts.lc_ldap_anonymous) constructor=lc_ldap.shortcuts.lc_ldap_anonymous)
def radius_event(f): def radius_event(fun):
"""Décorateur pour les fonctions d'interfaces avec radius. """Décorateur pour les fonctions d'interfaces avec radius.
Une telle fonction prend un uniquement argument, qui est une liste de tuples Une telle fonction prend un uniquement argument, qui est une liste de tuples
(clé, valeur) et renvoie un triplet dont les composantes sont : (clé, valeur) et renvoie un triplet dont les composantes sont :
@ -69,22 +99,23 @@ def radius_event(f):
et autres trucs du genre) et autres trucs du genre)
* un tuple de couples (clé, valeur) pour les valeurs internes à mettre à * un tuple de couples (clé, valeur) pour les valeurs internes à mettre à
jour (mot de passe par exemple) jour (mot de passe par exemple)
Voir des exemples plus complets ici:
https://github.com/FreeRADIUS/freeradius-server/blob/master/src/modules/rlm_python/
On se contente avec ce décorateur (pour l'instant) de convertir la liste de On se contente avec ce décorateur (pour l'instant) de convertir la liste de
tuples en entrée en un dictionnaire.""" tuples en entrée en un dictionnaire."""
def new_f(auth_data): def new_f(auth_data):
if type(auth_data) == dict:
data = auth_data
else:
data = dict() data = dict()
for (key, value) in auth_data or []: for (key, value) in auth_data or []:
# Beware: les valeurs scalaires sont entre guillemets # Beware: les valeurs scalaires sont entre guillemets
# Ex: Calling-Station-Id: "une_adresse_mac" # Ex: Calling-Station-Id: "une_adresse_mac"
data[key] = value.replace('"', '') data[key] = value.replace('"', '')
try: try:
return f(data) return fun(data)
except Exception as e: except Exception as err:
logger.error(repr(e) + ' on data ' + repr(auth_data)) logger.error('Failed %r on data %r' % (err, auth_data))
raise raise
return new_f return new_f
@ -104,18 +135,21 @@ def get_machines(data, conn, is_wifi=True, proprio=None):
try: try:
mac = lc_ldap.crans_utils.format_mac(mac.decode('ascii', 'ignore')) mac = lc_ldap.crans_utils.format_mac(mac.decode('ascii', 'ignore'))
except: except:
radiusd.radlog(radiusd.L_ERR, 'Cannot format MAC !') logger.error('Cannot format MAC !')
mac = None mac = None
username = data.get('User-Name', None) username = data.get('User-Name', None)
if username: if username:
# Pour les requètes venant de federezwifi
username = username.split('@', 1)[0]
username = escape_ldap(username.decode('ascii', 'ignore')) username = escape_ldap(username.decode('ascii', 'ignore'))
if username.endswith(suffix): if username.endswith(suffix):
username = username[:-len(suffix)] username = username[:-len(suffix)]
if mac is None: if mac is None:
radiusd.radlog(radiusd.L_ERR, 'Cannot read client MAC from AP !') logger.error('Cannot read mac from AP')
if username is None: if username is None:
radiusd.radlog(radiusd.L_ERR, 'Cannot read client User-Name !') logger.error('Cannot read client User-Name !')
# Liste de recherches ldap à essayer, dans l'ordre # Liste de recherches ldap à essayer, dans l'ordre
# ** Case 1: Search by mac # ** Case 1: Search by mac
@ -138,6 +172,9 @@ def get_machines(data, conn, is_wifi=True, proprio=None):
res = conn.search(u'(&%s(macAddress=<automatique>)(host=%s%s))' % res = conn.search(u'(&%s(macAddress=<automatique>)(host=%s%s))' %
(base, username, suffix), **opt) (base, username, suffix), **opt)
if TEST_SERVER:
res += conn.search(u'(&%s(host=%s%s))' %
(base, username, suffix), **opt)
return res return res
def get_prise_chbre(data): def get_prise_chbre(data):
@ -164,7 +201,7 @@ def get_prise_chbre(data):
try: try:
bat_name = nas[3].upper() bat_name = nas[3].upper()
bat_num = int(nas.split('-', 1)[1]) bat_num = int(nas.split('-', 1)[1])
except IndexError, ValueError: except (IndexError, ValueError):
pass pass
port = data.get('NAS-Port', None) port = data.get('NAS-Port', None)
if port: if port:
@ -182,7 +219,7 @@ def get_prise_chbre(data):
def realm_of_machine(machine): def realm_of_machine(machine):
"""Renvoie le `realm` d'une machine. Don't ask""" """Renvoie le `realm` d'une machine. Don't ask"""
if isinstance(machine, lc_ldap.objets.machineFixe): if isinstance(machine, lc_ldap.objets.machineFixe):
return 'fil' return 'adherents'
elif isinstance(machine, lc_ldap.objets.machineWifi): elif isinstance(machine, lc_ldap.objets.machineWifi):
return 'wifi-adh' return 'wifi-adh'
else: else:
@ -190,29 +227,29 @@ def realm_of_machine(machine):
def get_fresh_rid(machine): def get_fresh_rid(machine):
"""Génère un rid tout frais pour la machine. Fonction kludge""" """Génère un rid tout frais pour la machine. Fonction kludge"""
lockId = machine.conn.lockholder.newid() lock_id = machine.conn.lockholder.newid()
realm = realm_of_machine(machine) realm = realm_of_machine(machine)
try: try:
return machine.conn._find_id('rid', realm, lockId) return machine.conn._find_id('rid', realm, lock_id)
finally: finally:
machine.conn.lockholder.purge(lockId) machine.conn.lockholder.purge(lock_id)
@use_ldap_admin @use_ldap_admin
def register_mac(data, machine, conn): def register_machine(data, machine, conn):
"""Enregistre la mac actuelle sur une machine donnée.""" """Enregistre la mac actuelle et/ou assigne le rid sur une machine donnée."""
# TODO lc_ldap devrait posséder une fonction pour passer en rw depuis un ro # TODO lc_ldap devrait posséder une fonction pour passer en rw depuis un ro
if 'w' not in machine.mode: if 'w' not in machine.mode:
machine = conn.search(dn=machine.dn, scope=ldap.SCOPE_BASE, mode='rw')[0] machine = conn.search(dn=machine.dn, scope=ldap.SCOPE_BASE, mode='rw')[0]
mac = data.get('Calling-Station-Id', None) mac = data.get('Calling-Station-Id', None)
if mac is None: if mac is None:
radiusd.radlog(radiusd.L_ERR, 'Cannot find MAC') logger.warn('Cannot find MAC for registration (aborting)')
return return
mac = mac.decode('ascii', 'ignore').replace('"','') mac = mac.decode('ascii', 'ignore').replace('"','')
try: try:
mac = lc_ldap.crans_utils.format_mac(mac).lower() mac = lc_ldap.crans_utils.format_mac(mac).lower()
except: except Exception:
radiusd.radlog(radiusd.L_ERR, 'Cannot format MAC !') logger.warn('Cannot format MAC for registration (aborting)')
return return
with machine: with machine:
@ -233,13 +270,25 @@ def register_mac(data, machine, conn):
@radius_event @radius_event
@use_ldap_admin @use_ldap_admin
@use_ldap @use_ldap
def instantiate(p, *conns): def instantiate(*_):
"""Utile pour initialiser les connexions ldap une première fois (otherwise, """Utile pour initialiser les connexions ldap une première fois (otherwise,
do nothing)""" do nothing)"""
logger.info('Instantiation') logger.info('Instantiation')
if TEST_SERVER: if TEST_SERVER:
logger.info('DBG_FREERADIUS is enabled') logger.info('DBG_FREERADIUS is enabled')
@radius_event
def authorize(data):
"""Fonction qui aiguille entre nas, wifi et filaire pour authorize
On se contecte de faire une verification basique de ce que contien la requète
pour déterminer la fonction à utiliser"""
if data.get('NAS-Port-Type', '')==u'Ethernet':
return authorize_fil(data)
elif u"Wireless" in data.get('NAS-Port-Type', ''):
return authorize_wifi(data)
else:
return authorize_nas(data)
@radius_event @radius_event
def authorize_wifi(data): def authorize_wifi(data):
"""Section authorize pour le wifi """Section authorize pour le wifi
@ -251,26 +300,29 @@ def authorize_wifi(data):
items = get_machines(data) items = get_machines(data)
if not items: if not items:
radiusd.radlog(radiusd.L_ERR, 'lc_ldap: Nobody found') logger.error('No machine found in lc_ldap')
return radiusd.RLM_MODULE_NOTFOUND return radiusd.RLM_MODULE_NOTFOUND
if len(items) > 1: if len(items) > 1:
radiusd.radlog(radiusd.L_ERR, 'lc_ldap: Too many results (took first)') logger.warn('lc_ldap: Too many results (taking first)')
machine = items[0] machine = items[0]
proprio = machine.proprio() proprio = machine.proprio()
if isinstance(proprio, lc_ldap.objets.AssociationCrans): if isinstance(proprio, lc_ldap.objets.AssociationCrans):
radiusd.radlog(radiusd.L_ERR, 'Crans machine trying to authenticate !') logger.error('Crans machine trying to authenticate !')
return radiusd.RLM_MODULE_INVALID return radiusd.RLM_MODULE_INVALID
for bl in machine.blacklist_actif(): for bl in machine.blacklist_actif():
if bl.value['type'] in bl_reject: if bl.value['type'] in BL_REJECT:
return radiusd.RLM_MODULE_REJECT
# Kludge : vlan isolement pas possible, donc reject quand-même
if not WIFI_DYN_VLAN and bl.value['type'] in BL_ISOLEMENT:
return radiusd.RLM_MODULE_REJECT return radiusd.RLM_MODULE_REJECT
if not machine.get('ipsec', False): if not machine.get('ipsec', False):
radiusd.radlog(radiusd.L_ERR, 'WiFi authentication but machine has no' + logger.error('WiFi auth but machine has no password')
'password')
return radiusd.RLM_MODULE_REJECT return radiusd.RLM_MODULE_REJECT
password = machine['ipsec'][0].value.encode('ascii', 'ignore') password = machine['ipsec'][0].value.encode('ascii', 'ignore')
@ -285,17 +337,124 @@ def authorize_wifi(data):
@radius_event @radius_event
def authorize_fil(data): def authorize_fil(data):
"""For now, do nothing.
TODO: check bl_reject.
TODO: check chap auth
""" """
Check le challenge chap, et accepte.
TODO: check BL_REJECT.
"""
chap_ok = False
# Teste l'authentification chap fournie
# password et challenge doivent être données
# en hexa (avec ou sans le 0x devant)
# le User-Name est en réalité la mac ( xx:xx:xx:xx:xx )
password = data.get('CHAP-Password', '')
challenge = data.get('CHAP-Challenge', '')
mac = data.get('User-Name', '')
logger.debug('(fil) authorize(%r)' % ((password, challenge, mac),))
try:
challenge = binascii.a2b_hex(challenge.replace('0x',''))
password = binascii.a2b_hex(password.replace('0x',''))
if hashlib.md5(password[0] + mac + challenge).digest() == password[1:]:
logger.info("(fil) Chap ok")
chap_ok = True
else:
logger.info("(fil) Chap wrong")
except Exception as err:
logger.info("(fil) Chap challenge check failed with %r" % err)
if not chap_ok:
if TEST_SERVER:
logger.debug('(fil) Continue auth (debug)')
else:
return radiusd.RLM_MODULE_REJECT
return (radiusd.RLM_MODULE_UPDATED, return (radiusd.RLM_MODULE_UPDATED,
(), (),
( (
("Auth-Type", "crans_fil"), ("Auth-Type", "Accept"),
), ),
) )
def radius_password(secret_name, machine=None):
"""Cherche le mdp radius pour la machine donnée, et fallback sur le
secret canonique nommé"""
if machine and machine.has_key('TODO'):
pass
return secrets.get(secret_name)
@radius_event
@use_ldap
def authorize_nas(data, ldap):
"""Remplis le mdp d'une borne, ou d'un switch"""
logger.info('nas_auth with %r' % data)
ip = data.get('NAS-Identifier', '')
is_v6 = ':' in ip
ip_stm = ("FreeRADIUS-Client-IP%s-Address" % ('v6'*is_v6, ), ip)
# Find machine
# On rajoute les Machines du club federez au base_filter (federez-wifi):
fed = ldap.search(u'(nom=Federez)')[0]
mach_fed = fed.machines()
base_filter = u'(|(objectClass=machineCrans)(objectClass=borneWifi)'
for mach in mach_fed:
base_filter = base_filter + "(mid=%s)" % mach['mid'][0]
base_filter = base_filter + u')'
if is_v6:
addr = netaddr.IPAddress(ip).value
# EUI64, hein ?
assert ((addr >> 24) & 0xffff) == 0xfffe
# Extrait la mac de l'EUI64 (« trust me, it works »)
mac = (addr >> 16) & (0xffffff << 24) ^ (addr & 0xffffff) ^ (1 << 41)
mac = lc_ldap.crans_utils.format_mac("%012x" % mac)
m_filter = u'(macAddress=%s)' % mac
else:
m_filter = u'(ipHostNumber=%s)' % escape_ldap(ip)
machines = ldap.search(u'(&%s%s)' % (base_filter, m_filter))
if not machines:
if TEST_SERVER or ip == '127.0.0.1':
password = radius_password('radius_eap_key')
shortname = "wifi"
vserver = 'inner-tunnel'
else:
logger.info('not found %r' % m_filter)
return radiusd.RLM_MODULE_NOTFOUND
elif unicode(machines[0]['host'][0]).startswith(u'bat'):
password = radius_password('radius_key', machines[0])
shortname = 'switchs'
vserver = 'filaire'
else:
password = radius_password('radius_eap_key', machines[0])
shortname = "wifi"
vserver = 'wifi'
return (radiusd.RLM_MODULE_OK,
(),
(
ip_stm,
("FreeRADIUS-Client-Require-MA", "no"),
("FreeRADIUS-Client-Secret", password),
("FreeRADIUS-Client-Shortname", shortname),
("FreeRADIUS-Client-NAS-Type", "other"),
# On teste avec une équipe qui marche
("FreeRADIUS-Client-Virtual-Server", vserver),
),
)
@radius_event
def post_auth(data):
# On cherche quel est le type de machine, et quel sites lui appliquer
if data.get('NAS-Port-Type', '')==u'Ethernet':
return post_auth_fil(data)
elif u"Wireless" in data.get('NAS-Port-Type', ''):
return post_auth_wifi(data)
@radius_event @radius_event
def post_auth_wifi(data): def post_auth_wifi(data):
"""Appelé une fois que l'authentification est ok. """Appelé une fois que l'authentification est ok.
@ -308,14 +467,13 @@ def post_auth_wifi(data):
log_message = '(wifi) %s -> %s [%s%s]' % \ log_message = '(wifi) %s -> %s [%s%s]' % \
(port, mac, vlan_name, (reason and u': ' + reason).encode('utf-8')) (port, mac, vlan_name, (reason and u': ' + reason).encode('utf-8'))
logger.info(log_message) logger.info(log_message)
radiusd.radlog(radiusd.L_AUTH, log_message)
# Si NAS ayant des mapping particuliers, à signaler ici # Si NAS ayant des mapping particuliers, à signaler ici
vlan_id = config.vlans[vlan_name] vlan_id = config.vlans[vlan_name]
# WiFi : Pour l'instant, on ne met pas d'infos de vlans dans la réponse # WiFi : Pour l'instant, on ne met pas d'infos de vlans dans la réponse
# les bornes wifi ont du mal avec cela # les bornes wifi ont du mal avec cela
if TEST_SERVER: if WIFI_DYN_VLAN:
return (radiusd.RLM_MODULE_UPDATED, return (radiusd.RLM_MODULE_UPDATED,
( (
("Tunnel-Type", "VLAN"), ("Tunnel-Type", "VLAN"),
@ -338,7 +496,6 @@ def post_auth_fil(data):
log_message = '(fil) %s -> %s [%s%s]' % \ log_message = '(fil) %s -> %s [%s%s]' % \
(port, mac, vlan_name, (reason and u': ' + reason).encode('utf-8')) (port, mac, vlan_name, (reason and u': ' + reason).encode('utf-8'))
logger.info(log_message) logger.info(log_message)
radiusd.radlog(radiusd.L_AUTH, log_message)
# Si NAS ayant des mapping particuliers, à signaler ici # Si NAS ayant des mapping particuliers, à signaler ici
vlan_id = config.vlans[vlan_name] vlan_id = config.vlans[vlan_name]
@ -389,8 +546,8 @@ def decide_vlan(data, is_wifi, conn):
proprio = machine.proprio() proprio = machine.proprio()
# Avant de continuer, on assigne la mac à la machine candidat # Avant de continuer, on assigne la mac à la machine candidat
if '<automatique>' in machine['macAddress']: if '<automatique>' in machine['macAddress'] or not machine['rid']:
register_mac(data, machine) register_machine(data, machine)
if not machine['ipHostNumber']: if not machine['ipHostNumber']:
decision = 'v6only', u'No IPv4' decision = 'v6only', u'No IPv4'
@ -403,9 +560,9 @@ def decide_vlan(data, is_wifi, conn):
# Application des blacklists # Application des blacklists
for bl in machine.blacklist_actif(): for bl in machine.blacklist_actif():
if bl.value['type'] in bl_isolement: if bl.value['type'] in BL_ISOLEMENT:
decision = 'isolement', unicode(bl) decision = 'isolement', unicode(bl)
if bl.value['type'] in bl_accueil: if bl.value['type'] in BL_ACCUEIL:
decision = 'accueil', unicode(bl) decision = 'accueil', unicode(bl)
# Filaire : protection anti-"squattage" # Filaire : protection anti-"squattage"
@ -423,6 +580,8 @@ def decide_vlan(data, is_wifi, conn):
# Pour les locaux clubs, il n'y a pas forcément un club sédentaire # Pour les locaux clubs, il n'y a pas forcément un club sédentaire
# (typiquement, les locaux sous digicode) # (typiquement, les locaux sous digicode)
decision = decision[0], decision[1] + u' (local club)' decision = decision[0], decision[1] + u' (local club)'
elif chbre in PUBLIC_CHBRE:
decision = decision[0], decision[1] + u' (lieu de vie)'
else: else:
for hebergeur in hebergeurs: for hebergeur in hebergeurs:
# Si on est hébergé par un adhérent ok, ou que c'est notre # Si on est hébergé par un adhérent ok, ou que c'est notre
@ -455,28 +614,12 @@ def decide_vlan(data, is_wifi, conn):
return (port,) + decision return (port,) + decision
@radius_event @radius_event
def dummy_fun(p): def dummy_fun(_):
"""Do nothing, successfully. (C'est pour avoir un truc à mettre)"""
return radiusd.RLM_MODULE_OK return radiusd.RLM_MODULE_OK
def detach(p=None): def detach(_=None):
"""Appelé lors du déchargement du module (enfin, normalement)""" """Appelé lors du déchargement du module (enfin, normalement)"""
print "*** goodbye from auth.py ***" print "*** goodbye from auth.py ***"
return radiusd.RLM_MODULE_OK return radiusd.RLM_MODULE_OK
# à réimplémenter dans le authorize
# chap_ok(os.getenv('CHAP_PASSWORD'), os.getenv('CHAP_CHALLENGE'), mac)
def chap_ok(password, challenge, clear_pass) :
""" Test l'authentification chap fournie
password et chalenge doivent être données
en hexa (avec ou sans le 0x devant)
retourne True si l'authentification est OK
retourne False sinon
"""
try :
challenge = binascii.a2b_hex(challenge.replace('0x',''))
password = binascii.a2b_hex(password.replace('0x',''))
if hashlib.md5(password[0] + clear_pass + challenge).digest() == password[1:] :
return True
except :
return False

View file

@ -0,0 +1,31 @@
# Define a network where clients may be dynamically defined.
client dynamic {
#
# You MUST specify a netmask!
# IPv4 /32 or IPv6 /128 are NOT allowed!
ipv6addr = 0::
netmask = 0
#
# Define the virtual server used to discover dynamic clients.
dynamic_clients = dynamic_clients
#
# Define the lifetime (in seconds) for dynamic clients.
# They will be cached for this lifetime, and deleted afterwards.
#
# If the lifetime is "0", then the dynamic client is never
# deleted. The only way to delete the client is to re-start
# the server.
lifetime = 3600
}
# Le même, en ipv4
client dynamic {
ipaddr = 0.0.0.0
netmask = 0
dynamic_clients = dynamic_clients
lifetime = 3600
}

View file

@ -0,0 +1 @@
../rlm_python_unifie.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

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

@ -0,0 +1,17 @@
#
# This is the virtual server referenced above by "dynamic_clients".
server dynamic_clients {
#
# The only contents of the virtual server is the "authorize" section.
authorize {
# Hack dégueux: crans_nas est un backend python. Or, rlm_python ne
# fournit pas en entrée les variables "control", uniquement les variables
# "request", du coup on met ce qui nous intéresse là.
update request {
NAS-Identifier = "%{Packet-Src-IP-Address:-%{Packet-Src-IPv6-Address}}"
}
crans_unifie
}
}

View file

@ -0,0 +1,20 @@
######################################################################
#
# Authentification filaire du crans
#
######################################################################
server filaire {
authorize{
preprocess
crans_unifie
}
authenticate{
crans_unifie
}
post-auth{
crans_unifie
}
}

View file

@ -0,0 +1,397 @@
# -*- text -*-
######################################################################
#
# This is a virtual server that handles *only* inner tunnel
# requests for EAP-TTLS and PEAP types.
#
# $Id: inner-tunnel,v 1.6 2008/03/29 21:33:12 aland Exp $
#
######################################################################
server inner-tunnel {
# Authorization. First preprocess (hints and huntgroups files),
# then realms, and finally look in the "users" file.
#
# The order of the realm modules will determine the order that
# we try to find a matching realm.
#
# Make *sure* that 'preprocess' comes before any realm if you
# need to setup hints for the remote radius server
authorize {
#preprocess
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
#chap
#
# Pull crypt'd passwords from /etc/passwd or /etc/shadow,
# using the system API's to get the password. If you want
# to read /etc/passwd or /etc/shadow directly, see the
# passwd module, above.
#
#unix
#
# Look for IPASS style 'realm/', and if not found, look for
# '@realm', and decide whether or not to proxy, based on
# that.
# IPASS
#
# If you are using multiple kinds of realms, you probably
# want to set "ignore_null = yes" for all of them.
# Otherwise, when the first style of realm doesn't match,
# the other styles won't be checked.
#
# Note that proxying the inner tunnel authentication means
# that the user MAY use one identity in the outer session
# (e.g. "anonymous", and a different one here
# (e.g. "user@example.com"). The inner session will then be
# proxied elsewhere for authentication. If you are not
# careful, this means that the user can cause you to forward
# the authentication to another RADIUS server, and have the
# accounting logs *not* sent to the other server. This makes
# it difficult to bill people for their network activity.
#
#suffix
# ntdomain
#
# The "suffix" module takes care of stripping the domain
# (e.g. "@example.com") from the User-Name attribute, and the
# next few lines ensure that the request is not proxied.
#
# If you want the inner tunnel request to be proxied, delete
# the next few lines.
#
#update control {
# Proxy-To-Realm := LOCAL
#}
#
# This module takes care of EAP-MSCHAPv2 authentication.
#
# It also sets the EAP-Type attribute in the request
# attribute list to the EAP type from the packet.
#
# The example below uses module failover to avoid querying all
# of the following modules if the EAP module returns "ok".
# Therefore, your LDAP and/or SQL servers will not be queried
# for the many packets that go back and forth to set up TTLS
# or PEAP. The load on those servers will therefore be reduced.
#
eap {
ok = return
}
#
# Read the 'users' file
# files
#
# Look in an SQL database. The schema of the database
# is meant to mirror the "users" file.
#
# See "Authorization Queries" in sql.conf
# sql
#
# If you are using /etc/smbpasswd, and are also doing
# mschap authentication, the un-comment this line, and
# configure the 'etc_smbpasswd' module, above.
# etc_smbpasswd
#
# The ldap module will set Auth-Type to LDAP if it has not
# already been set
#ldap
#
# If the users are logging in with an MS-CHAP-Challenge
# attribute for authentication, the mschap module will find
# the MS-CHAP-Challenge attribute, and add 'Auth-Type := MS-CHAP'
# to the request, which will cause the server to then use
# the mschap module for authentication.
mschap
#
# Enforce daily limits on time spent logged in.
# daily
#
# Use the checkval module
# checkval
#expiration
#logintime
#
# If no other module has claimed responsibility for
# authentication, then try to use PAP. This allows the
# other modules listed above to add a "known good" password
# to the request, and to do nothing else. The PAP module
# will then see that password, and use it to do PAP
# authentication.
#
# This module should be listed last, so that the other modules
# get a chance to set Auth-Type for themselves.
#
#pap
}
# Authentication.
#
#
# This section lists which modules are available for authentication.
# Note that it does NOT mean 'try each module in order'. It means
# that a module from the 'authorize' section adds a configuration
# attribute 'Auth-Type := FOO'. That authentication type is then
# used to pick the apropriate module from the list below.
#
# In general, you SHOULD NOT set the Auth-Type attribute. The server
# will figure it out on its own, and will do the right thing. The
# most common side effect of erroneously setting the Auth-Type
# attribute is that one authentication method will work, but the
# others will not.
#
# The common reasons to set the Auth-Type attribute by hand
# is to either forcibly reject the user, or forcibly accept him.
#
authenticate {
#
# PAP authentication, when a back-end database listed
# in the 'authorize' section supplies a password. The
# password can be clear-text, or encrypted.
#Auth-Type PAP {
# pap
#}
#
# Most people want CHAP authentication
# A back-end database listed in the 'authorize' section
# MUST supply a CLEAR TEXT password. Encrypted passwords
# won't work.
Auth-Type CHAP {
chap
}
#
# MSCHAP authentication.
Auth-Type MS-CHAP {
mschap
}
#
# Pluggable Authentication Modules.
# pam
#
# See 'man getpwent' for information on how the 'unix'
# module checks the users password. Note that packets
# containing CHAP-Password attributes CANNOT be authenticated
# against /etc/passwd! See the FAQ for details.
#
# unix
# Uncomment it if you want to use ldap for authentication
#
# Note that this means "check plain-text password against
# the ldap database", which means that EAP won't work,
# as it does not supply a plain-text password.
#Auth-Type LDAP {
# ldap
#}
#
# Allow EAP authentication.
eap
}
######################################################################
#
# There are no accounting requests inside of EAP-TTLS or PEAP
# tunnels.
#
######################################################################
# Session database, used for checking Simultaneous-Use. Either the radutmp
# or rlm_sql module can handle this.
# The rlm_sql module is *much* faster
session {
# radutmp
#
# See "Simultaneous Use Checking Queries" in sql.conf
# sql
}
# Post-Authentication
# Once we KNOW that the user has been authenticated, there are
# additional steps we can take.
post-auth {
crans_unifie
# Note that we do NOT assign IP addresses here.
# If you try to assign IP addresses for EAP authentication types,
# it WILL NOT WORK. You MUST use DHCP.
#
# If you want to have a log of authentication replies,
# un-comment the following line, and the 'detail reply_log'
# section, above.
# reply_log
#
# After authenticating the user, do another SQL query.
#
# See "Authentication Logging Queries" in sql.conf
# sql
#
# Instead of sending the query to the SQL server,
# write it into a log file.
#
# sql_log
#
# Un-comment the following if you have set
# 'edir_account_policy_check = yes' in the ldap module sub-section of
# the 'modules' section.
#
# ldap
#
# Access-Reject packets are sent through the REJECT sub-section of the
# post-auth section.
#
# Add the ldap module name (or instance) if you have set
# 'edir_account_policy_check = yes' in the ldap module configuration
#
Post-Auth-Type REJECT {
# attr_filter.access_reject
}
#
# The example policy below updates the outer tunnel reply
# (usually Access-Accept) with the User-Name from the inner
# tunnel User-Name. Since this section is processed in the
# context of the inner tunnel, "request" here means "inner
# tunnel request", and "outer.reply" means "outer tunnel
# reply attributes".
#
# This example is most useful when the outer session contains
# a User-Name of "anonymous@....", or a MAC address. If it
# is enabled, the NAS SHOULD use the inner tunnel User-Name
# in subsequent accounting packets. This makes it easier to
# track user sessions, as they will all be based on the real
# name, and not on "anonymous".
#
# The problem with doing this is that it ALSO exposes the
# real user name to any intermediate proxies. People use
# "anonymous" identifiers outside of the tunnel for a very
# good reason: it gives them more privacy. Setting the reply
# to contain the real user name removes ALL privacy from
# their session.
#
# If you want privacy to remain, see the
# Chargeable-User-Identity attribute from RFC 4372. In order
# to use that attribute, you will have to allocate a
# per-session identifier for the user, and store it in a
# long-term database (e.g. SQL). You should also use that
# attribute INSTEAD of the configuration below.
#
#update outer.reply {
# User-Name = "%{request:User-Name}"
#}
}
#
# When the server decides to proxy a request to a home server,
# the proxied request is first passed through the pre-proxy
# stage. This stage can re-write the request, or decide to
# cancel the proxy.
#
# Only a few modules currently have this method.
#
pre-proxy {
# attr_rewrite
# Uncomment the following line if you want to change attributes
# as defined in the preproxy_users file.
# files
# Uncomment the following line if you want to filter requests
# sent to remote servers based on the rules defined in the
# 'attrs.pre-proxy' file.
# attr_filter.pre-proxy
# If you want to have a log of packets proxied to a home
# server, un-comment the following line, and the
# 'detail pre_proxy_log' section, above.
# pre_proxy_log
}
#
# When the server receives a reply to a request it proxied
# to a home server, the request may be massaged here, in the
# post-proxy stage.
#
post-proxy {
# If you want to have a log of replies from a home server,
# un-comment the following line, and the 'detail post_proxy_log'
# section, above.
# post_proxy_log
# attr_rewrite
# Uncomment the following line if you want to filter replies from
# remote proxies based on the rules defined in the 'attrs' file.
# attr_filter.post-proxy
#
# If you are proxying LEAP, you MUST configure the EAP
# module, and you MUST list it here, in the post-proxy
# stage.
#
# You MUST also use the 'nostrip' option in the 'realm'
# configuration. Otherwise, the User-Name attribute
# in the proxied request will not match the user name
# hidden inside of the EAP packet, and the end server will
# reject the EAP request.
#
eap
#
# If the server tries to proxy a request and fails, then the
# request is processed through the modules in this section.
#
# The main use of this section is to permit robust proxying
# of accounting packets. The server can be configured to
# proxy accounting packets as part of normal processing.
# Then, if the home server goes down, accounting packets can
# be logged to a local "detail" file, for processing with
# radrelay. When the home server comes back up, radrelay
# will read the detail file, and send the packets to the
# home server.
#
# With this configuration, the server always responds to
# Accounting-Requests from the NAS, but only writes
# accounting packets to disk if the home server is down.
#
# Post-Proxy-Type Fail {
# detail
# }
}
} # inner-tunnel server block

View file

@ -0,0 +1,466 @@
######################################################################
#
# Authentification wifi du crans
#
######################################################################
#
# Authorization. First preprocess (hints and huntgroups files),
# then realms, and finally look in the "users" file.
#
# The order of the realm modules will determine the order that
# we try to find a matching realm.
#
# Make *sure* that 'preprocess' comes before any realm if you
# need to setup hints for the remote radius server
server wifi {
authorize {
if (User-Name !~ /crans$/) {
if (User-Name =~ /^(.*)@(.*)/) {
update control {
Proxy-To-Realm := 'FEDEREZ'
}
}
}
#
# The preprocess module takes care of sanitizing some bizarre
# attributes in the request, and turning them into attributes
# which are more standard.
#
# It takes care of processing the 'raddb/hints' and the
# 'raddb/huntgroups' files.
#
# It also adds the %{Client-IP-Address} attribute to the request.
#preprocess
#
# If you want to have a log of authentication requests,
# un-comment the following line, and the 'detail auth_log'
# section, above.
# auth_log
#
# The chap module will set 'Auth-Type := CHAP' if we are
# handling a CHAP request and Auth-Type has not already been set
# chap
#
# If the users are logging in with an MS-CHAP-Challenge
# attribute for authentication, the mschap module will find
# the MS-CHAP-Challenge attribute, and add 'Auth-Type := MS-CHAP'
# to the request, which will cause the server to then use
# the mschap module for authentication.
#mschap
#
# If you have a Cisco SIP server authenticating against
# FreeRADIUS, uncomment the following line, and the 'digest'
# line in the 'authenticate' section.
# digest
#
# Look for IPASS style 'realm/', and if not found, look for
# '@realm', and decide whether or not to proxy, based on
# that.
# IPASS
#
# If you are using multiple kinds of realms, you probably
# want to set "ignore_null = yes" for all of them.
# Otherwise, when the first style of realm doesn't match,
# the other styles won't be checked.
#
# suffix
# ntdomain
#
# This module takes care of EAP-MD5, EAP-TLS, and EAP-LEAP
# authentication.
#
# It also sets the EAP-Type attribute in the request
# attribute list to the EAP type from the packet.
#
# As of 2.0, the EAP module returns "ok" in the authorize stage
# for TTLS and PEAP. In 1.x, it never returned "ok" here, so
# this change is compatible with older configurations.
#
# The example below uses module failover to avoid querying all
# of the following modules if the EAP module returns "ok".
# Therefore, your LDAP and/or SQL servers will not be queried
# for the many packets that go back and forth to set up TTLS
# or PEAP. The load on those servers will therefore be reduced.
#
eap {
ok = return
}
#
# Pull crypt'd passwords from /etc/passwd or /etc/shadow,
# using the system API's to get the password. If you want
# to read /etc/passwd or /etc/shadow directly, see the
# passwd module in radiusd.conf.
#
# unix
#
# Read the 'users' file
# files
#
# Look in an SQL database. The schema of the database
# is meant to mirror the "users" file.
#
# See "Authorization Queries" in sql.conf
# sql
#
# If you are using /etc/smbpasswd, and are also doing
# mschap authentication, the un-comment this line, and
# configure the 'etc_smbpasswd' module, above.
# etc_smbpasswd
#
# The ldap module will set Auth-Type to LDAP if it has not
# already been set
#ldap
#
# Enforce daily limits on time spent logged in.
# daily
#
# Use the checkval module
# checkval
# expiration
# logintime
#
# If no other module has claimed responsibility for
# authentication, then try to use PAP. This allows the
# other modules listed above to add a "known good" password
# to the request, and to do nothing else. The PAP module
# will then see that password, and use it to do PAP
# authentication.
#
# This module should be listed last, so that the other modules
# get a chance to set Auth-Type for themselves.
#
#pap
#
# If "status_server = yes", then Status-Server messages are passed
# through the following section, and ONLY the following section.
# This permits you to do DB queries, for example. If the modules
# listed here return "fail", then NO response is sent.
#
# Autz-Type Status-Server {
#
# }
}
# Authentication.
#
#
# This section lists which modules are available for authentication.
# Note that it does NOT mean 'try each module in order'. It means
# that a module from the 'authorize' section adds a configuration
# attribute 'Auth-Type := FOO'. That authentication type is then
# used to pick the apropriate module from the list below.
#
# In general, you SHOULD NOT set the Auth-Type attribute. The server
# will figure it out on its own, and will do the right thing. The
# most common side effect of erroneously setting the Auth-Type
# attribute is that one authentication method will work, but the
# others will not.
#
# The common reasons to set the Auth-Type attribute by hand
# is to either forcibly reject the user (Auth-Type := Reject),
# or to or forcibly accept the user (Auth-Type := Accept).
#
# Note that Auth-Type := Accept will NOT work with EAP.
#
# Please do not put "unlang" configurations into the "authenticate"
# section. Put them in the "post-auth" section instead. That's what
# the post-auth section is for.
#
authenticate {
#
# PAP authentication, when a back-end database listed
# in the 'authorize' section supplies a password. The
# password can be clear-text, or encrypted.
#Auth-Type PAP {
# pap
#}
#
# Most people want CHAP authentication
# A back-end database listed in the 'authorize' section
# MUST supply a CLEAR TEXT password. Encrypted passwords
# won't work.
#Auth-Type CHAP {
# chap
#}
#
# MSCHAP authentication.
Auth-Type MS-CHAP {
mschap
}
#
# If you have a Cisco SIP server authenticating against
# FreeRADIUS, uncomment the following line, and the 'digest'
# line in the 'authorize' section.
# digest
#
# Pluggable Authentication Modules.
# pam
#
# See 'man getpwent' for information on how the 'unix'
# module checks the users password. Note that packets
# containing CHAP-Password attributes CANNOT be authenticated
# against /etc/passwd! See the FAQ for details.
#
# unix
# Uncomment it if you want to use ldap for authentication
#
# Note that this means "check plain-text password against
# the ldap database", which means that EAP won't work,
# as it does not supply a plain-text password.
#Auth-Type LDAP {
# ldap
#}
#
# Allow EAP authentication.
eap
}
#
# Pre-accounting. Decide which accounting type to use.
#
preacct {
preprocess
#
# Ensure that we have a semi-unique identifier for every
# request, and many NAS boxes are broken.
acct_unique
#
# Look for IPASS-style 'realm/', and if not found, look for
# '@realm', and decide whether or not to proxy, based on
# that.
#
# Accounting requests are generally proxied to the same
# home server as authentication requests.
# IPASS
suffix
# ntdomain
#
# Read the 'acct_users' file
# files
}
#
# Accounting. Log the accounting data.
#
accounting {
#
# Create a 'detail'ed log of the packets.
# Note that accounting requests which are proxied
# are also logged in the detail file.
# detail
# daily
# Update the wtmp file
#
# If you don't use "radlast", you can delete this line.
# unix
#
# For Simultaneous-Use tracking.
#
# Due to packet losses in the network, the data here
# may be incorrect. There is little we can do about it.
# radutmp
# sradutmp
# Return an address to the IP Pool when we see a stop record.
# main_pool
#
# Log traffic to an SQL database.
#
# See "Accounting queries" in sql.conf
# sql
#
# Instead of sending the query to the SQL server,
# write it into a log file.
#
# sql_log
# Cisco VoIP specific bulk accounting
# pgsql-voip
# Filter attributes from the accounting response.
# attr_filter.accounting_response
#
# See "Autz-Type Status-Server" for how this works.
#
# Acct-Type Status-Server {
#
# }
}
# Session database, used for checking Simultaneous-Use. Either the radutmp
# or rlm_sql module can handle this.
# The rlm_sql module is *much* faster
session {
# radutmp
#
# See "Simultaneous Use Checking Queries" in sql.conf
# sql
}
# Post-Authentication
# Once we KNOW that the user has been authenticated, there are
# additional steps we can take.
post-auth {
# Get an address from the IP Pool.
# main_pool
#
# If you want to have a log of authentication replies,
# un-comment the following line, and the 'detail reply_log'
# section, above.
# reply_log
#
# After authenticating the user, do another SQL query.
#
# See "Authentication Logging Queries" in sql.conf
# sql
#
# Instead of sending the query to the SQL server,
# write it into a log file.
#
# sql_log
#
# Un-comment the following if you have set
# 'edir_account_policy_check = yes' in the ldap module sub-section of
# the 'modules' section.
#
#ldap
# exec
#
# Access-Reject packets are sent through the REJECT sub-section of the
# post-auth section.
#
# Add the ldap module name (or instance) if you have set
# 'edir_account_policy_check = yes' in the ldap module configuration
#
Post-Auth-Type REJECT {
# attr_filter.access_reject
}
}
#
# When the server decides to proxy a request to a home server,
# the proxied request is first passed through the pre-proxy
# stage. This stage can re-write the request, or decide to
# cancel the proxy.
#
# Only a few modules currently have this method.
#
pre-proxy {
# attr_rewrite
# Uncomment the following line if you want to change attributes
# as defined in the preproxy_users file.
# files
# Uncomment the following line if you want to filter requests
# sent to remote servers based on the rules defined in the
# 'attrs.pre-proxy' file.
# attr_filter.pre-proxy
# If you want to have a log of packets proxied to a home
# server, un-comment the following line, and the
# 'detail pre_proxy_log' section, above.
# pre_proxy_log
}
#
# When the server receives a reply to a request it proxied
# to a home server, the request may be massaged here, in the
# post-proxy stage.
#
post-proxy {
# If you want to have a log of replies from a home server,
# un-comment the following line, and the 'detail post_proxy_log'
# section, above.
# post_proxy_log
# attr_rewrite
# Uncomment the following line if you want to filter replies from
# remote proxies based on the rules defined in the 'attrs' file.
# attr_filter.post-proxy
#
# If you are proxying LEAP, you MUST configure the EAP
# module, and you MUST list it here, in the post-proxy
# stage.
#
# You MUST also use the 'nostrip' option in the 'realm'
# configuration. Otherwise, the User-Name attribute
# in the proxied request will not match the user name
# hidden inside of the EAP packet, and the end server will
# reject the EAP request.
#
eap
#
# If the server tries to proxy a request and fails, then the
# request is processed through the modules in this section.
#
# The main use of this section is to permit robust proxying
# of accounting packets. The server can be configured to
# proxy accounting packets as part of normal processing.
# Then, if the home server goes down, accounting packets can
# be logged to a local "detail" file, for processing with
# radrelay. When the home server comes back up, radrelay
# will read the detail file, and send the packets to the
# home server.
#
# With this configuration, the server always responds to
# Accounting-Requests from the NAS, but only writes
# accounting packets to disk if the home server is down.
#
# Post-Proxy-Type Fail {
# detail
# }
}
}

1
freeradius/testing/auth.py Symbolic link
View file

@ -0,0 +1 @@
../auth.py

View file

@ -2,6 +2,10 @@
# #
# Definitions for RADIUS programs # Definitions for RADIUS programs
# #
# This file should *NOT* be available in production mode : importing this dummy
# module in place of the radiusd module exposed by freeradius avoid logging
# function radlog to work.
#
# Copyright 2002 Miguel A.L. Paraz <mparaz@mparaz.com> # Copyright 2002 Miguel A.L. Paraz <mparaz@mparaz.com>
# #
# This should only be used when testing modules. # This should only be used when testing modules.

View file

@ -16,9 +16,10 @@ delattr(sys, 'argv')
auth.instantiate(()) auth.instantiate(())
# Test avec l'interface wifi d'apprentis
p=( p=(
('Calling-Station-Id', 'b0:79:94:cf:d1:9a'), ('Calling-Station-Id', '02:69:75:42:24:03'),
('User-Name', 'moo-torola'), ('User-Name', 'apprentis-wifi'),
) )
print repr(auth.authorize_wifi(p)) print repr(auth.authorize_wifi(p))

16
freeradius/testing/test_inner Executable file
View file

@ -0,0 +1,16 @@
#!/bin/bash
# Teste le inner-tunnel en se connectant directement au serveur
MAC=moo-torola
PASSWORD=7syxqbbkdb
#SECRET=`PYTHONPATH=/etc/crans/secrets/ python -c \
# "import secrets; print secrets.radius_eap_key"`
SECRET=e4hmraqw6Yps
NAS_NAME=atree.wifi.crans.org
#SERVER=127.0.0.1
SERVER=pea.v6.wifi.crans.org
SERVER=138.231.136.35
SERVER=[2a01:240:fe3d:c04:0:70ff:fe65:6103]
SERVER=localhost
radtest -t mschap -x -4 $MAC $PASSWORD $SERVER 18 $SECRET $SECRET $NAS_NAME

View file

@ -94,10 +94,6 @@ def dialog(backtitle, arg, dialogrc=''):
# Récupération du contenu du pipe # Récupération du contenu du pipe
_, sortie = processus.communicate() _, sortie = processus.communicate()
# On décode la sortie du programme dialog (et on le fait ici parce que
# c'est ici l'interface).
sortie = to_unicode(sortie)
resultat = sortie.splitlines() resultat = sortie.splitlines()
# Récupération du code d'erreur # Récupération du code d'erreur

View file

@ -8,10 +8,6 @@
# Contenu : # Contenu :
# --------- # ---------
# #
# Décorateur :
# static_var([(name, val)]), un décorateur pour créer des variables
# statiques dans une fonction
#
# Fonctions : # Fonctions :
# getTerminalSize(), une fonction qui récupère le couple # getTerminalSize(), une fonction qui récupère le couple
# largeur, hauteur du terminal courant. # largeur, hauteur du terminal courant.
@ -39,12 +35,13 @@ import os
import fcntl import fcntl
import termios import termios
import struct import struct
import functools
import time import time
import re import re
from locale import getpreferredencoding from locale import getpreferredencoding
from cranslib.decorators import static_var
OCT_NAMES = ["Pio", "Tio", "Gio", "Mio", "Kio"] OCT_NAMES = ["Pio", "Tio", "Gio", "Mio", "Kio"]
OCT_SIZES = [1024**(len(OCT_NAMES) - i) for i in xrange(0, len(OCT_NAMES))] OCT_SIZES = [1024**(len(OCT_NAMES) - i) for i in xrange(0, len(OCT_NAMES))]
TERM_FORMAT = '\x1b\[[0-1];([0-9]|[0-9][0-9])m' TERM_FORMAT = '\x1b\[[0-1];([0-9]|[0-9][0-9])m'
@ -56,7 +53,9 @@ def try_decode(string):
avoir en réception. avoir en réception.
""" """
unicode_str = "" if isinstance(string, unicode):
return string
try: try:
return string.decode("UTF-8") return string.decode("UTF-8")
except UnicodeDecodeError: except UnicodeDecodeError:
@ -88,21 +87,6 @@ def guess_preferred_encoding():
return encoding return encoding
def static_var(couples):
"""Decorator setting static variable
to a function.
"""
# Using setattr magic, we set static
# variable on function. This avoid
# computing stuff again.
def decorate(fun):
functools.wraps(fun)
for (name, val) in couples:
setattr(fun, name, val)
return fun
return decorate
def getTerminalSize(): def getTerminalSize():
"""Dummy function to get term dimensions. """Dummy function to get term dimensions.
Thanks to http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python Thanks to http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python
@ -276,7 +260,7 @@ def nostyle(dialog=False):
return "\Zn" return "\Zn"
return "\033[1;0m" return "\033[1;0m"
@static_var([("styles", {})]) @static_var(("styles", {}))
def style(texte, what=None, dialog=False): def style(texte, what=None, dialog=False):
"""Pretty text is pretty """Pretty text is pretty
On peut appliquer plusieurs styles d'affilée, ils seront alors traités On peut appliquer plusieurs styles d'affilée, ils seront alors traités
@ -574,7 +558,26 @@ if __name__ == "__main__":
time.sleep(1) time.sleep(1)
prettyDoin("Les carottes sont cuites." , "Ok") 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") titres = ("Nom", "Prénom", "Âge", "Adresse")
longueurs = [25, 25, '*', '*'] longueurs = [25, 25, '*', '*']
print tableau(data, titres, longueurs).encode(guess_preferred_encoding()) print tableau(data, titres, longueurs).encode(guess_preferred_encoding())

View file

@ -2,8 +2,11 @@
import os import os
import psycopg2 import psycopg2
from functools import wraps from functools import wraps
import time import time
import socket
conn = None conn = None
# : échec définitif, on raise une exception direct # : échec définitif, on raise une exception direct
@ -19,17 +22,19 @@ def _need_conn(f):
raise NameError("La connexion à la pase postgresql ne peut être établie.") raise NameError("La connexion à la pase postgresql ne peut être établie.")
attempts = 0 attempts = 0
while not conn or not attempts: while not conn or not attempts:
if __name__.endswith('annuaires_pg_test') or os.getenv('DBG_ANNUAIRE', False): host = os.getenv('DBG_ANNUAIRE', 'pgsql.v4.adm.crans.org')
# Test habituel sur vo:
if host == '1' or __name__.endswith('annuaires_pg_test'):
host='localhost' host='localhost'
else:
host='pgsql.v4.adm.crans.org'
# "connecting …" # "connecting …"
try: try:
if not conn: if not conn:
if attempts: if attempts:
# Attend un peu avant de reessayer # Attend un peu avant de reessayer
time.sleep(delay) time.sleep(delay)
conn = psycopg2.connect(user='crans', database='switchs', conn = psycopg2.connect(user='crans_ro', database='django',
host=host) host=host)
return f(*args, **kwargs) return f(*args, **kwargs)
except psycopg2.OperationalError: except psycopg2.OperationalError:
@ -42,7 +47,8 @@ def _need_conn(f):
# backend pgsql. On utilise donc une exception plus standard # backend pgsql. On utilise donc une exception plus standard
return first_connect return first_connect
bat_switchs = ["a", "b", "c", "g", "h", "i", "j", "m", "o", "p"] # Le v est virtuel.
bat_switchs = ["a", "b", "c", "g", "h", "i", "j", "m", "o", "p", "v"]
class ChbreNotFound(ValueError): class ChbreNotFound(ValueError):
"""Lorsqu'une chambre n'existe pas""" """Lorsqu'une chambre n'existe pas"""
@ -55,14 +61,14 @@ def chbre_prises(batiment, chambre = None):
if chambre: if chambre:
chambre = chambre.lower() chambre = chambre.lower()
cur = conn.cursor() cur = conn.cursor()
cur.execute("SELECT prise_crans FROM prises WHERE (batiment, chambre) = (%s, %s)", (batiment, chambre)) cur.execute("SELECT prise_crans FROM prises_prise WHERE (batiment, chambre) = (%s, %s)", (batiment, chambre))
try: try:
return "%03d" % cur.fetchone()[0] return "%03d" % cur.fetchone()[0]
except TypeError: except TypeError:
raise ChbreNotFound("Chambre inexistante bat %r, chbre %r" % (batiment, chambre)) raise ChbreNotFound("Chambre inexistante bat %r, chbre %r" % (batiment, chambre))
else: else:
cur = conn.cursor() cur = conn.cursor()
cur.execute("SELECT chambre, prise_crans FROM prises WHERE batiment = %s", batiment) cur.execute("SELECT chambre, prise_crans FROM prises_prise WHERE batiment = %s", batiment)
ret = {} ret = {}
for chambre, prise_crans in cur.fetchall(): for chambre, prise_crans in cur.fetchall():
ret[chambre] = "%03d" % prise_crans ret[chambre] = "%03d" % prise_crans
@ -76,7 +82,7 @@ def chbre_commentaire(batiment, chambre):
global conn global conn
batiment = batiment.lower() batiment = batiment.lower()
cur = conn.cursor() cur = conn.cursor()
cur.execute("SELECT commentaire FROM prises WHERE (batiment, chambre) = (%s,%s)", (batiment, chambre)) cur.execute("SELECT commentaire FROM prises_prise WHERE (batiment, chambre) = (%s,%s)", (batiment, chambre))
try: try:
return cur.fetchone()[0] return cur.fetchone()[0]
except TypeError: except TypeError:
@ -88,14 +94,14 @@ def reverse(batiment, prise = None):
batiment = batiment.lower() batiment = batiment.lower()
if prise: if prise:
cur = conn.cursor() cur = conn.cursor()
cur.execute("SELECT chambre FROM prises WHERE (batiment, prise_crans) = (%s, %s)", (batiment, int(prise))) cur.execute("SELECT chambre FROM prises_prise WHERE (batiment, prise_crans) = (%s, %s)", (batiment, int(prise)))
try: try:
return [chbre for (chbre,) in cur.fetchall()] return [chbre for (chbre,) in cur.fetchall()]
except TypeError: except TypeError:
raise ValueError("Prise %s inexistante" % prise) raise ValueError("Prise %s inexistante" % prise)
else: else:
cur = conn.cursor() cur = conn.cursor()
cur.execute("SELECT chambre, prise_crans FROM prises WHERE batiment = %s", batiment) cur.execute("SELECT chambre, prise_crans FROM prises_prise WHERE batiment = %s", batiment)
ret = {} ret = {}
for chambre, prise_crans in cur.fetchall(): for chambre, prise_crans in cur.fetchall():
try: try:
@ -106,37 +112,15 @@ def reverse(batiment, prise = None):
if not ret: if not ret:
raise ValueError("Batiment %s inexistant" % batiment) raise ValueError("Batiment %s inexistant" % batiment)
return ret return ret
@_need_conn
def is_crans(batiment, chambre):
"""Chambre cablee au Cr@ns ?"""
batiment = batiment.lower()
chambre = chambre.lower()
cur = conn.cursor()
cur.execute("SELECT crans FROM prises WHERE (batiment, chambre) = (%s, %s)", (batiment, chambre))
return cur.fetchone()[0]
@_need_conn @_need_conn
def is_connected(batiment, chambre): def is_connected(batiment, chambre):
"""Cablage physique effectue ?""" """Cablage physique effectue ?"""
batiment = batiment.lower() batiment = batiment.lower()
chambre = chambre.lower() chambre = chambre.lower()
cur = conn.cursor() cur = conn.cursor()
cur.execute("SELECT cablage_effectue FROM prises WHERE (batiment, chambre) = (%s, %s)", (batiment, chambre)) cur.execute("SELECT cablage_effectue FROM prises_prise WHERE (batiment, chambre) = (%s, %s)", (batiment, chambre))
return cur.fetchone()[0] return cur.fetchone()[0]
@_need_conn
def crous_to_crans(batiment, chambre):
"""Passage d'une chambre de CROUS a Cr@ns"""
batiment = batiment.lower()
chambre = chambre.lower()
if is_crans(batiment, chambre):
return
cur = conn.cursor()
cur.execute("UPDATE prises SET (crans, crous, cablage_effectue) = (TRUE, FALSE, not cablage_effectue) WHERE (batiment, chambre) = (%s, %s)", (batiment, chambre))
conn.commit()
cur.close()
# Prises d'uplink, de machines du crans / Prises d'utilité CRANS # Prises d'uplink, de machines du crans / Prises d'utilité CRANS
uplink_prises={ 'a' : uplink_prises={ 'a' :
{ 49 : 'uplink->bata-4', 50 : 'libre-service', { 49 : 'uplink->bata-4', 50 : 'libre-service',
@ -153,7 +137,8 @@ uplink_prises={ 'a' :
349 : 'uplink->batb-4', 350 : 'libre-service', 349 : 'uplink->batb-4', 350 : 'libre-service',
401 : 'uplink->batb-0', 402 : 'uplink->batb-1', 401 : 'uplink->batb-0', 402 : 'uplink->batb-1',
403 : 'uplink->batb-2', 404 : 'uplink->batb-3', 403 : 'uplink->batb-2', 404 : 'uplink->batb-3',
405 : 'uplink->backbone' }, 405 : 'uplink->backbone', 523 : 'uplink->batb-4',
},
'c' : 'c' :
{ 49 : 'uplink->batc-3', 50 : 'libre-service', { 49 : 'uplink->batc-3', 50 : 'libre-service',
149 : 'uplink->batc-3', 150 : 'libre-service', 149 : 'uplink->batc-3', 150 : 'libre-service',
@ -289,11 +274,35 @@ uplink_prises={ 'a' :
_SPECIAL_SWITCHES = ['backbone.adm.crans.org', _SPECIAL_SWITCHES = ['backbone.adm.crans.org',
'multiprise-v6.adm.crans.org', 'multiprise-v6.adm.crans.org',
'batk-0.crans.org', 'batk-0.crans.org',
'batp-4.adm.crans.org',
'minigiga.adm.crans.org', 'minigiga.adm.crans.org',
'batb-5.crans.org',
]
_HIDDEN_SWITCHES = [
'batp-4.adm.crans.org',
'batv-0.adm.crans.org',
] ]
def all_switchs(bat=None, hide=_SPECIAL_SWITCHES): 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. """Retourne la liste des switchs pour un batiment.
Si bat est donné, seulement pour le bâtiment demandé, sinon pour Si bat est donné, seulement pour le bâtiment demandé, sinon pour
@ -302,14 +311,19 @@ def all_switchs(bat=None, hide=_SPECIAL_SWITCHES):
simplement batx""" simplement batx"""
if bat == None: if bat == None:
bat = bat_switchs bat = list(bat_switchs)
if type(bat) not in [ tuple, list ] : if type(bat) not in [ tuple, list ] :
bat = [bat] bat = [bat]
switchs = [] switchs = []
for b in bat: for b in bat:
indexes = set(n/100 for n in uplink_prises[b]) indexes = set(n/100 for n in uplink_prises[b])
for i in indexes: for i in indexes:
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: if hostname not in hide:
switchs.append(hostname) switchs.append(hostname)
# on ajoute quand-même le backbone et/ou multiprise-v6 si demandé # on ajoute quand-même le backbone et/ou multiprise-v6 si demandé

View file

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

View file

@ -25,7 +25,6 @@ import lc_ldap.attributs
import lc_ldap.objets import lc_ldap.objets
import gestion.mail as mail_module import gestion.mail as mail_module
encoding = getattr(sys.stdout, 'encoding', "UTF-8")
current_user = os.getenv("SUDO_USER") or os.getenv("USER") or os.getenv("LOGNAME") or getpass.getuser() current_user = os.getenv("SUDO_USER") or os.getenv("USER") or os.getenv("LOGNAME") or getpass.getuser()
def check_password(password, no_cracklib=False, dialog=False): def check_password(password, no_cracklib=False, dialog=False):
@ -39,15 +38,16 @@ def check_password(password, no_cracklib=False, dialog=False):
password.decode('ascii') password.decode('ascii')
except UnicodeDecodeError: except UnicodeDecodeError:
problem = True problem = True
if not dialog: msg += u"Le mot de passe ne doit contenir que des caractères ascii.\n"
affich_tools.cprint(u'Le mot de passe ne doit contenir que des caractères ascii.', "rouge")
else: if len(password) >= 64:
msg += affich_tools.coul(u'Le mot de passe ne doit contenir que des caractères ascii.\n', "rouge", dialog=dialog) problem = True
msg += u"Le mot de passe doit faire strictement moins de 64 caractères\n"
# Nounou mode # Nounou mode
if no_cracklib: if no_cracklib:
if len(password) >= config.password.root_min_len: if len(password) >= config.password.root_min_len:
return True return True, msg
else: else:
upp = 0 upp = 0
low = 0 low = 0
@ -67,37 +67,22 @@ def check_password(password, no_cracklib=False, dialog=False):
# Recherche de manque de caractères # Recherche de manque de caractères
if cif < config.password.min_cif: if cif < config.password.min_cif:
if not dialog: msg += u'Le mot de passe doit contenir plus de chiffres.\n'
affich_tools.cprint(u'Le mot de passe doit contenir plus de chiffres.', "rouge")
else:
msg += affich_tools.coul(u'Le mot de passe doit contenir plus de chiffres.\n', "rouge", dialog=dialog)
problem = True problem = True
if upp < config.password.min_upp: if upp < config.password.min_upp:
if not dialog: msg += u'Le mot de passe doit contenir plus de majuscules.\n'
affich_tools.cprint(u'Le mot de passe doit contenir plus de majuscules.', "rouge")
else:
msg += affich_tools.coul(u'Le mot de passe doit contenir plus de majuscules.\n', "rouge", dialog=dialog)
problem = True problem = True
if low < config.password.min_low: if low < config.password.min_low:
if not dialog: msg += u'Le mot de passe doit contenir plus de minuscules.\n'
affich_tools.cprint(u'Le mot de passe doit contenir plus de minuscules.', "rouge")
else:
msg += affich_tools.coul(u'Le mot de passe doit contenir plus de minuscules.\n', "rouge", dialog=dialog)
problem = True problem = True
if oth < config.password.min_oth: if oth < config.password.min_oth:
if not dialog: msg += u'Le mot de passe doit contenir plus de caractères qui ne sont ni des chiffres, ni des majuscules, ni des minuscules.\n'
affich_tools.cprint(u'Le mot de passe doit contenir plus de caractères qui ne sont ni des chiffres, ni des majuscules, ni des minuscules.', "rouge")
else:
msg += affich_tools.coul(u'Le mot de passe doit contenir plus de caractères qui ne sont ni des chiffres, ni des majuscules, ni des minuscules.\n', "rouge", dialog=dialog)
problem = True problem = True
# Scores sur la longueur # Scores sur la longueur
longueur = config.password.upp_value*upp + config.password.low_value*low + config.password.cif_value*cif + config.password.oth_value*oth longueur = config.password.upp_value*upp + config.password.low_value*low + config.password.cif_value*cif + config.password.oth_value*oth
if longueur < config.password.min_len: if longueur < config.password.min_len:
if not dialog: msg += u'Le mot de passe devrait être plus long, ou plus difficile.\n'
affich_tools.cprint(u'Le mot de passe devrait être plus long, ou plus difficile.', "rouge")
else:
msg += affich_tools.coul(u'Le mot de passe devrait être plus long, ou plus difficile.\n', "rouge", dialog=dialog)
problem = True problem = True
if not problem: if not problem:
@ -111,31 +96,46 @@ def check_password(password, no_cracklib=False, dialog=False):
# Le mot vient-il du dico (à améliorer, on voudrait pouvoir préciser # Le mot vient-il du dico (à améliorer, on voudrait pouvoir préciser
# la rigueur du test) ? # la rigueur du test) ?
password = cracklib.VeryFascistCheck(password) password = cracklib.VeryFascistCheck(password)
if dialog:
msg = affich_tools.coul(msg, 'rouge', dialog=dialog)
return True, msg return True, msg
except ValueError as e: except ValueError as e:
if not dialog: msg += str(e).decode(config.in_encoding)
affich_tools.cprint(e.message, "rouge")
else: if dialog:
msg += affich_tools.coul(str(e).decode(), "rouge", dialog=dialog) msg = affich_tools.coul(msg, 'rouge', dialog=dialog)
return False, msg return False, msg
else: else:
if dialog:
msg = affich_tools.coul(msg, 'rouge', dialog=dialog)
return True, msg return True, msg
else: else:
if dialog:
msg = affich_tools.coul(msg, 'rouge', dialog=dialog)
return False, msg return False, msg
if dialog:
msg = affich_tools.coul(msg, 'rouge', dialog=dialog)
return False, msg return False, msg
@lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5, constructor=lc_ldap.shortcuts.lc_ldap_admin) @lc_ldap.shortcuts.with_ldap_conn(retries=2, delay=5, constructor=lc_ldap.shortcuts.lc_ldap_admin)
def change_password(ldap, login=None, verbose=False, no_cracklib=False, **args): def change_password(ldap, login=None, verbose=False, no_cracklib=False, **kwargs):
""" """
Change le mot de passe en fonction des arguments Change le mot de passe en fonction des arguments
""" """
if login is None: if login is None:
login = current_user login = current_user
if type(login) == str: if type(login) == str:
login = login.decode(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) login = lc_ldap.crans_utils.escape(login)
query = ldap.search(u"(uid=%s)" % login, mode="w") query = ldap.search(u"(uid=%s)" % login, mode="w")
if not query: if not query:
affich_tools.cprint('Utilisateur introuvable dans la base de données, modification de l\'utilisateur local.', "rouge") affich_tools.cprint('Utilisateur introuvable dans la base de données, modification de l\'utilisateur local.', "rouge")
sys.exit(2) sys.exit(2)
@ -145,7 +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['userPassword'] = [lc_ldap.crans_utils.hash_password("test").decode('ascii')]
user.cancel() user.cancel()
except EnvironmentError as e: except EnvironmentError as e:
affich_tools.cprint(str(e).decode(encoding), "rouge") affich_tools.cprint(str(e).decode(config.in_encoding), "rouge")
# Génération d'un mail # Génération d'un mail
From = 'roots@crans.org' From = 'roots@crans.org'
@ -155,7 +155,7 @@ To: %s
Subject: Tentative de changement de mot de passe ! Subject: Tentative de changement de mot de passe !
Tentative de changement du mot de passe de %s par %s. Tentative de changement du mot de passe de %s par %s.
""" % (From, To , login.encode(encoding), current_user) """ % (From, To, login.encode(config.out_encoding), current_user)
# Envoi mail # Envoi mail
with mail_module.ServerConnection() as conn: with mail_module.ServerConnection() as conn:
@ -167,19 +167,23 @@ Tentative de changement du mot de passe de %s par %s.
prenom = "Club" prenom = "Club"
else: else:
prenom = user['prenom'][0] prenom = user['prenom'][0]
affich_tools.cprint("Changement du mot de passe de %s %s." % affich_tools.cprint(
(prenom, user['nom'][0]), "Changement du mot de passe de %s %s." % (
"vert") prenom,
user['nom'][0]
),
"vert",
)
# Règles du jeu # Règles du jeu
# (J'ai perdu) # (J'ai perdu)
if verbose: if verbose:
affich_tools.cprint(u"""Règles : affich_tools.cprint(
u"""Règles :
Longueur standard : %s, root : %s, Longueur standard : %s, root : %s,
Minimums : chiffres : %s, minuscules : %s, majuscules : %s, autres : %s, Minimums : chiffres : %s, minuscules : %s, majuscules : %s, autres : %s,
Scores de longueur : chiffres : %s, minuscules : %s, majuscules : %s, autres : %s, Scores de longueur : chiffres : %s, minuscules : %s, majuscules : %s, autres : %s,
Cracklib : %s.""" % ( Cracklib : %s.""" % (config.password.min_len,
config.password.min_len,
config.password.root_min_len, config.password.root_min_len,
config.password.min_cif, config.password.min_cif,
config.password.min_low, config.password.min_low,
@ -189,32 +193,37 @@ Cracklib : %s.""" % (
config.password.low_value, config.password.low_value,
config.password.upp_value, config.password.upp_value,
config.password.oth_value, config.password.oth_value,
"Oui" * (not no_cracklib) + "Non" * (no_cracklib) "Oui" * (not no_cracklib) + "Non" * (no_cracklib),
), ),
'jaune') 'jaune',
)
else: 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 ne doit pas être basé sur un mot du dictionnaire.
Il doit contenir au moins %s chiffre(s), %s minuscule(s), Il doit contenir au moins %s chiffre(s), %s minuscule(s),
%s majuscule(s) et au moins %s autre(s) caractère(s). %s majuscule(s) et au moins %s autre(s) caractère(s).
CTRL+D ou CTRL+C provoquent un abandon.""" % CTRL+D ou CTRL+C provoquent un abandon.""" % (config.password.min_len,
(
config.password.min_len,
config.password.min_cif, config.password.min_cif,
config.password.min_low, config.password.min_low,
config.password.min_upp, config.password.min_upp,
config.password.min_oth config.password.min_oth
), 'jaune') ),
'jaune',
)
try: try:
while True: while True:
mdp = getpass.getpass("Nouveau mot de passe: ") mdp = getpass.getpass("Nouveau mot de passe: ")
if check_password(mdp, no_cracklib)[0]: (ret, msg) = check_password(mdp, no_cracklib)
if ret:
mdp2 = getpass.getpass("Retaper le mot de passe: ") mdp2 = getpass.getpass("Retaper le mot de passe: ")
if mdp != mdp2: if mdp != mdp2:
affich_tools.cprint(u"Les deux mots de passe diffèrent.", "rouge") affich_tools.cprint(u"Les deux mots de passe diffèrent.", "rouge")
else: else:
break break
else:
affich_tools.cprint(msg, 'rouge')
except KeyboardInterrupt: except KeyboardInterrupt:
affich_tools.cprint(u'\nAbandon', 'rouge') affich_tools.cprint(u'\nAbandon', 'rouge')
@ -231,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") affich_tools.cprint(u"Mot de passe de %s changé." % (user['uid'][0]), "vert")
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(description="Recherche dans la base des adhérents",
description="Recherche dans la base des adhérents", add_help=False,
add_help=False) )
parser.add_argument('-h', '--help', parser.add_argument('-h',
'--help',
help="Affiche ce message et quitte.", help="Affiche ce message et quitte.",
action="store_true") action="store_true",
parser.add_argument('-n', '--no-cracklib', )
parser.add_argument('-n',
'--no-cracklib',
help="Permet de contourner les règles de choix du mot de passe" + help="Permet de contourner les règles de choix du mot de passe" +
"(réservé aux nounous).", "(réservé aux nounous).",
action="store_true") action="store_true",
parser.add_argument('-v', '--verbose', )
parser.add_argument('-v',
'--verbose',
help="Permet de contourner les règles de choix du mot de passe" + help="Permet de contourner les règles de choix du mot de passe" +
"(réservé aux nounous).", "(réservé aux nounous).",
action="store_true") action="store_true",
parser.add_argument('login', type=str, nargs="?", )
help="L'utilisateur dont on veut changer le mot de passe.") parser.add_argument('login',
type=str,
nargs="?",
help="L'utilisateur dont on veut changer le mot de passe.",
)
args = parser.parse_args() args = parser.parse_args()
if args.help: if args.help:
parser.print_help() parser.print_help()
sys.exit(0) sys.exit(0)
if args.no_cracklib:
if not lc_ldap.attributs.nounou in ldap.droits:
args.no_cracklib = False
change_password(**vars(args)) change_password(**vars(args))

View file

@ -10,29 +10,28 @@
import os, sys import os, sys
from gestion.affich_tools import prompt from gestion.affich_tools import prompt
from gestion.ldap_crans import crans_ldap
db = crans_ldap() from lc_ldap import shortcuts
ldap = shortcuts.lc_ldap_admin()
uid = os.getenv('SUDO_UID') uid = os.getenv('SUDO_UID')
if not uid : if not uid :
print "Impossible de déterminer l'utilisateur" print "Impossible de déterminer l'utilisateur"
sys.exit(1) sys.exit(1)
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 try:
club = s['club'] adh = adh[0]
if len(club) == 1 : except IndexError:
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 :
print 'Erreur fatale lors de la consultation de la base LDAP' print 'Erreur fatale lors de la consultation de la base LDAP'
sys.exit(3) 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 :') shell = prompt(u'Nouveau shell :')
fd=open('/etc/shells') fd=open('/etc/shells')
lines=fd.readlines() lines=fd.readlines()
@ -45,7 +44,9 @@ if not shell in shells:
print '\n'.join(shells) print '\n'.join(shells)
sys.exit(4) sys.exit(4)
adh.chsh(shell) with adh as ad:
adh.save() ad['loginShell']=shell
ad.save()
# A cause de nscd # A cause de nscd
print "La modification sera prise en compte dans l'heure suivante." print "La modification sera prise en compte dans l'heure suivante."

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