Compare commits
506 commits
cleanup-ge
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1637a4aae4 | ||
![]() |
93d505745c | ||
![]() |
7b26e38606 | ||
![]() |
fe6f71acbc | ||
![]() |
cf76003c1f | ||
![]() |
16777e3914 | ||
![]() |
a1256091b3 | ||
![]() |
346861139c | ||
![]() |
8f715ff771 | ||
![]() |
525c77c2ce | ||
![]() |
0918366bb8 | ||
![]() |
fdca02be27 | ||
![]() |
229cd97fce | ||
![]() |
e7ba95b36d | ||
![]() |
3418dbb1b1 | ||
![]() |
450f6c49aa | ||
![]() |
daedb2b657 | ||
![]() |
8c00727456 | ||
![]() |
e67b3be24b | ||
![]() |
5e5a819b47 | ||
![]() |
4dd908e746 | ||
![]() |
69bff1c2da | ||
![]() |
baf79bdda7 | ||
![]() |
af0e971764 | ||
![]() |
14167847db | ||
![]() |
8b86e04dba | ||
![]() |
cc0b6bf013 | ||
![]() |
9ea84a34bc | ||
![]() |
cb0fda8c6c | ||
![]() |
023b354848 | ||
![]() |
bb0bedf1fd | ||
![]() |
b9bd5ab1fc | ||
![]() |
46b5aca99f | ||
![]() |
bbcd49c88c | ||
![]() |
4279b3db23 | ||
![]() |
565a2ae8ed | ||
![]() |
f52422622a | ||
![]() |
e16ff77675 | ||
![]() |
77ca50c48c | ||
![]() |
ad8846920e | ||
![]() |
09497c7c7a | ||
![]() |
20ddcb12bc | ||
![]() |
730db0107e | ||
![]() |
d989c9966c | ||
![]() |
8647650078 | ||
![]() |
dba410ceaf | ||
![]() |
b36b5ba2ce | ||
![]() |
73617312b8 | ||
![]() |
1eed679e40 | ||
![]() |
28cfce0916 | ||
![]() |
f943cf52a6 | ||
![]() |
9bfe935522 | ||
![]() |
43cdcc9c6f | ||
![]() |
e2141e1043 | ||
![]() |
19b13c39d7 | ||
![]() |
9194364ad0 | ||
![]() |
b88ba2a34f | ||
![]() |
ef95c2b159 | ||
![]() |
fc1b2c0edf | ||
![]() |
310356eba9 | ||
![]() |
06d97c9aa3 | ||
![]() |
51c0140dfb | ||
![]() |
e4a36fb702 | ||
![]() |
182597ca35 | ||
![]() |
f6609980fc | ||
![]() |
df4d564ffb | ||
![]() |
6f52c57431 | ||
![]() |
f4e5ce3104 | ||
![]() |
d58b92b957 | ||
![]() |
22bf89eef0 | ||
![]() |
b552af7739 | ||
![]() |
246cba6107 | ||
![]() |
b14847c625 | ||
![]() |
079831f5f3 | ||
![]() |
f975e60636 | ||
![]() |
b2f1f9c038 | ||
![]() |
712b256943 | ||
![]() |
7a9dd65698 | ||
![]() |
fbe99ab65c | ||
![]() |
d49c87b6c3 | ||
![]() |
32e4416687 | ||
![]() |
2860fe931c | ||
![]() |
6d4f6b0864 | ||
![]() |
44361f86ce | ||
![]() |
196bc1db68 | ||
![]() |
7d8f7c79ca | ||
![]() |
f3186853ec | ||
![]() |
1969b7909b | ||
![]() |
99003ebaf3 | ||
![]() |
e4c2a6e3bd | ||
![]() |
26a82dbb92 | ||
![]() |
abaea5ee8d | ||
![]() |
53bddc87b2 | ||
![]() |
d7c15ab14b | ||
![]() |
a527a6f394 | ||
![]() |
9f7c8ceaf3 | ||
![]() |
af9d15bcc6 | ||
![]() |
955dd379c7 | ||
![]() |
6ae6e54101 | ||
![]() |
ba6baa40dc | ||
![]() |
50415d4a90 | ||
![]() |
d4f67daea0 | ||
![]() |
a732024852 | ||
![]() |
ade4a9d82e | ||
![]() |
e7ba8c346d | ||
![]() |
22a05d3e1d | ||
![]() |
e2b8fd08f8 | ||
![]() |
588eba0515 | ||
![]() |
ef0e8bb22e | ||
![]() |
3dd845d333 | ||
![]() |
ea4d25f597 | ||
![]() |
5c6386abae | ||
![]() |
5b8ad633b3 | ||
![]() |
7aa2b6b688 | ||
![]() |
6a51e0c520 | ||
![]() |
ed91745fec | ||
![]() |
a363b6927d | ||
![]() |
947047dea0 | ||
![]() |
e46f6ba4d8 | ||
![]() |
c700ec4c03 | ||
![]() |
c5c903b114 | ||
![]() |
6da84bbfd1 | ||
![]() |
ca77b3defa | ||
![]() |
87382fbf6f | ||
![]() |
4d7585574f | ||
![]() |
1df6eb37f5 | ||
![]() |
2c1674038e | ||
![]() |
13555c735d | ||
![]() |
31f21003e7 | ||
![]() |
bd4403d8ee | ||
![]() |
cb2a389077 | ||
![]() |
1a06bcf4f9 | ||
![]() |
86991c6f5f | ||
![]() |
553b5f39ce | ||
![]() |
d8d31462f7 | ||
![]() |
5271e51f30 | ||
![]() |
de8832a3f9 | ||
![]() |
87d971ac7e | ||
![]() |
3abcb7b144 | ||
![]() |
c2a21b8756 | ||
![]() |
52a4cefdb5 | ||
![]() |
98b591978c | ||
![]() |
c653f97c03 | ||
![]() |
4624363443 | ||
![]() |
467565b90f | ||
![]() |
5bb370bffb | ||
![]() |
1e769cc9a9 | ||
![]() |
0492338bf8 | ||
![]() |
9e934c2e8c | ||
![]() |
9507536d3e | ||
![]() |
fbf76bec6b | ||
![]() |
59186fe37e | ||
![]() |
43d6795b53 | ||
![]() |
1d7b04a264 | ||
![]() |
4b165cef91 | ||
![]() |
16bc0eab01 | ||
![]() |
b53a92b2a7 | ||
![]() |
4e10e930cd | ||
![]() |
e7aa590314 | ||
![]() |
c9b3198e26 | ||
![]() |
587b76d930 | ||
![]() |
4915d64e90 | ||
![]() |
80fff2f826 | ||
![]() |
de52de5059 | ||
![]() |
94185a8f81 | ||
![]() |
062114d4ea | ||
![]() |
1262b47fb3 | ||
![]() |
96a8ceca8d | ||
![]() |
18d6e674f1 | ||
![]() |
11b2586819 | ||
![]() |
be8247667f | ||
![]() |
9c89e7af0a | ||
![]() |
ecbe29d3fb | ||
![]() |
fe9f69d19a | ||
![]() |
4ee1ea55d1 | ||
![]() |
1c675f251c | ||
![]() |
fe37e87532 | ||
![]() |
f1bcfde617 | ||
![]() |
29cb2a38f1 | ||
![]() |
f527a12064 | ||
![]() |
4874b2eebc | ||
![]() |
e2eb01f2aa | ||
![]() |
d57588e313 | ||
![]() |
dd1178d7d3 | ||
![]() |
13b7bfd936 | ||
![]() |
55aac0e164 | ||
![]() |
85f9ea46bb | ||
![]() |
7c2543d90e | ||
![]() |
7ed5d5e829 | ||
![]() |
394597c186 | ||
![]() |
c035723e54 | ||
![]() |
6f21fde1ea | ||
![]() |
628036e8da | ||
![]() |
6913629672 | ||
![]() |
f77a73218d | ||
![]() |
72bde6ca25 | ||
![]() |
b10d2d1e95 | ||
![]() |
b949b42364 | ||
![]() |
00d67f1fc3 | ||
![]() |
5a7e367e9c | ||
![]() |
037ecab023 | ||
![]() |
82b8fea9da | ||
![]() |
2a5c273b2c | ||
![]() |
a3c3290a26 | ||
![]() |
241b760921 | ||
![]() |
8c41ce5a5b | ||
![]() |
3944441033 | ||
![]() |
395db97395 | ||
![]() |
6161a4bc32 | ||
![]() |
3f70d1b825 | ||
![]() |
89c8f33f68 | ||
![]() |
aac1b74dbf | ||
![]() |
20cb9cdad0 | ||
![]() |
9b255bbb30 | ||
![]() |
118fa4700e | ||
![]() |
4b7a8be09b | ||
![]() |
914390a891 | ||
![]() |
79182f2b42 | ||
![]() |
0adfe858cc | ||
![]() |
4c20f02f73 | ||
![]() |
13d8829e0d | ||
![]() |
8c503584f9 | ||
![]() |
c3ba567ec8 | ||
![]() |
fcfdfbced9 | ||
![]() |
8db871f115 | ||
![]() |
12c09f10b6 | ||
![]() |
cc4c7c68c8 | ||
![]() |
36a299caa8 | ||
![]() |
2bc93d071b | ||
![]() |
a2a8538b61 | ||
![]() |
8d250bdc27 | ||
![]() |
6c7c4ecf74 | ||
![]() |
0b12a81e93 | ||
![]() |
ae803ba2c3 | ||
![]() |
93d6b2d175 | ||
![]() |
8d8164b80d | ||
![]() |
adeead08fc | ||
![]() |
0774fb688f | ||
![]() |
6258828e31 | ||
![]() |
c0a2697aea | ||
![]() |
9cb259ea71 | ||
![]() |
2d76cea2f9 | ||
![]() |
c34ebc5177 | ||
![]() |
94ee83b86a | ||
![]() |
a5ddd73ce5 | ||
![]() |
21176bfcf9 | ||
![]() |
f66fa61993 | ||
![]() |
9485236f2f | ||
![]() |
7e072aca3c | ||
![]() |
dd32a30814 | ||
![]() |
8f3f9c13da | ||
![]() |
a9593fe783 | ||
![]() |
39409ec894 | ||
![]() |
686bc45ed5 | ||
![]() |
96676dee69 | ||
![]() |
fb5f0888de | ||
![]() |
62d9379f03 | ||
![]() |
09a720b827 | ||
![]() |
5f0247ffb3 | ||
![]() |
f8e7ffb3ed | ||
![]() |
6d44f7e538 | ||
![]() |
b9cecfbc9e | ||
![]() |
c6e3bf4f44 | ||
![]() |
5568fc8bc7 | ||
![]() |
cce8a7ff37 | ||
![]() |
c36a513747 | ||
![]() |
0602ee1c5c | ||
![]() |
22511a52a7 | ||
![]() |
5f1a1b1397 | ||
![]() |
eac34dfc55 | ||
![]() |
0907ab98d3 | ||
![]() |
1ae148671d | ||
![]() |
6648b2e009 | ||
![]() |
8a08dc2cc1 | ||
![]() |
e087de4f57 | ||
![]() |
818ff14d56 | ||
![]() |
4504a3925d | ||
![]() |
29fdd087c3 | ||
![]() |
f53e64a14d | ||
![]() |
b72cc11447 | ||
![]() |
9d53c8e882 | ||
![]() |
6b2f41214f | ||
![]() |
d457162767 | ||
![]() |
0a3c9a0324 | ||
![]() |
4c8980a377 | ||
![]() |
418aa7b79b | ||
![]() |
52c3795255 | ||
![]() |
ebeab8a0a0 | ||
![]() |
650f55e7e8 | ||
![]() |
beb09f265b | ||
![]() |
7efb8a4db6 | ||
![]() |
8539e8c38d | ||
![]() |
dfa75aee76 | ||
![]() |
fbc9a4ef5c | ||
![]() |
8a015e0d16 | ||
![]() |
8d410af8a3 | ||
![]() |
a09ef77d22 | ||
![]() |
1c205fca93 | ||
![]() |
67bf2363cf | ||
![]() |
14a1905de3 | ||
![]() |
5c3045d5cb | ||
![]() |
6499e6802a | ||
![]() |
d3677b832a | ||
![]() |
e23b18c356 | ||
![]() |
ec94ee2e1d | ||
![]() |
6b63e030a6 | ||
![]() |
6b64af3314 | ||
![]() |
7ca15d7527 | ||
![]() |
32136b25fa | ||
![]() |
12e755fa64 | ||
![]() |
607c1b8855 | ||
![]() |
7ab4485d54 | ||
![]() |
372aa16c4f | ||
![]() |
971abfd950 | ||
![]() |
1df794db38 | ||
![]() |
937aab00af | ||
![]() |
9788cadfec | ||
![]() |
7762315e30 | ||
![]() |
8c8ac24c54 | ||
![]() |
2bcce4911f | ||
![]() |
dc37719f0d | ||
![]() |
2bbdfc7fc9 | ||
![]() |
3214dd0213 | ||
![]() |
a416c24e22 | ||
![]() |
cef0e444ca | ||
![]() |
2842b24649 | ||
![]() |
7b56a15685 | ||
![]() |
a6c38a88bf | ||
![]() |
2f815c63bb | ||
![]() |
e53877f8b2 | ||
![]() |
3965df79d0 | ||
![]() |
351565faaf | ||
![]() |
18b7e20efb | ||
![]() |
d501143825 | ||
![]() |
265e5d6b2d | ||
![]() |
deeafe477e | ||
![]() |
36613a1dc7 | ||
![]() |
bbf4ad6ade | ||
![]() |
85785c1f80 | ||
![]() |
71bc9bbc54 | ||
![]() |
fd86e567a9 | ||
![]() |
a16ab6f9d7 | ||
![]() |
65350221da | ||
![]() |
1e62621fcc | ||
![]() |
6740e96610 | ||
![]() |
a70205526f | ||
![]() |
2838a60501 | ||
![]() |
9da0007d40 | ||
![]() |
21c079865f | ||
![]() |
4045539f69 | ||
![]() |
689a0cd152 | ||
![]() |
5b8aca460e | ||
![]() |
1dde48ff32 | ||
![]() |
6534241650 | ||
![]() |
877137942a | ||
![]() |
77558c1f25 | ||
![]() |
77d98bf3fa | ||
![]() |
a800a65793 | ||
![]() |
e4857523d0 | ||
![]() |
9078c80d34 | ||
![]() |
7c98e01e56 | ||
![]() |
ec7f6d392f | ||
![]() |
729be231ee | ||
![]() |
8745a89176 | ||
![]() |
1d82e0c95c | ||
![]() |
65760e5ddb | ||
![]() |
08007c623e | ||
![]() |
1df9d480af | ||
![]() |
3fb338e937 | ||
![]() |
8b3832355e | ||
![]() |
d23bda8bd2 | ||
![]() |
8cc8770ee4 | ||
![]() |
0a580381cf | ||
![]() |
d81637eca6 | ||
![]() |
03519e4acd | ||
![]() |
887d54c133 | ||
![]() |
bad450fe3c | ||
![]() |
04b91024d9 | ||
![]() |
ddfc320741 | ||
![]() |
00b24a8a3d | ||
![]() |
b1df8cfc19 | ||
![]() |
9b0bf76621 | ||
![]() |
a94d180cde | ||
![]() |
57d4e52395 | ||
![]() |
f67d38baee | ||
![]() |
7e5cd649eb | ||
![]() |
d394c688b6 | ||
![]() |
7dac00f8b3 | ||
![]() |
edde503c9e | ||
![]() |
c1660df7ae | ||
![]() |
3e90c78c16 | ||
![]() |
b65466de08 | ||
![]() |
4205d73132 | ||
![]() |
a0f0c80ead | ||
![]() |
cd5ae8aaa5 | ||
![]() |
831cebb84d | ||
![]() |
a3bf7c7349 | ||
![]() |
0bdd06d8f4 | ||
![]() |
3b3e554509 | ||
![]() |
73aa1fe7eb | ||
![]() |
18bcbd19ea | ||
![]() |
0d1eca2f8c | ||
![]() |
b5990eaa47 | ||
![]() |
bf98167a63 | ||
![]() |
d6bb56dfaa | ||
![]() |
1726b669af | ||
![]() |
25022bde92 | ||
![]() |
66f3fd0707 | ||
![]() |
ef7e52d892 | ||
![]() |
1b68e5f5fd | ||
![]() |
0c4a999b1f | ||
![]() |
ab2cf5b6e4 | ||
![]() |
a742a6847c | ||
![]() |
ffb4a1d94f | ||
![]() |
4f7f75286c | ||
![]() |
66d17ded88 | ||
![]() |
f8f7c789a9 | ||
![]() |
1d3b136bed | ||
![]() |
746e5db7b8 | ||
![]() |
2993f31fd8 | ||
![]() |
d9bd6a621d | ||
![]() |
23c9df55eb | ||
![]() |
edfdd8ccc6 | ||
![]() |
d4cde74149 | ||
![]() |
407c1e4799 | ||
![]() |
5ef0534167 | ||
![]() |
baa9855327 | ||
![]() |
fdfdd23022 | ||
![]() |
93ee03aee9 | ||
![]() |
3041a8ee16 | ||
![]() |
9c472f97c0 | ||
![]() |
d6633b1258 | ||
![]() |
38cf8eb8e9 | ||
![]() |
6093c61b42 | ||
![]() |
64bb5252ea | ||
![]() |
83a1a611ef | ||
![]() |
0410d42147 | ||
![]() |
21e9851203 | ||
![]() |
900d8db1a7 | ||
![]() |
54e7d79d44 | ||
![]() |
6ec03b7e0e | ||
![]() |
d184d948a7 | ||
![]() |
1793b26ae2 | ||
![]() |
d31c24bcb9 | ||
![]() |
842bbc145b | ||
![]() |
b2afd29bb9 | ||
![]() |
7046a8cc87 | ||
![]() |
002031b18d | ||
![]() |
d6b359e717 | ||
![]() |
9824bb160a | ||
![]() |
b04b8deda5 | ||
![]() |
7bfa903078 | ||
![]() |
e1e51b2240 | ||
![]() |
bbf4dcd8dd | ||
![]() |
f31603f830 | ||
![]() |
33eac6ffac | ||
![]() |
3f1b7401ca | ||
![]() |
394531a537 | ||
![]() |
e0cced3e4d | ||
![]() |
891fae63f6 | ||
![]() |
cdf2c98228 | ||
![]() |
b1ea6070bc | ||
![]() |
ae83dac934 | ||
![]() |
ec603f1202 | ||
![]() |
182ac4ad32 | ||
![]() |
86a054cbad | ||
![]() |
ab17c9bdcd | ||
![]() |
3d5abd7fb3 | ||
![]() |
b470c860c0 | ||
![]() |
c0aa3c0e48 | ||
![]() |
cfac2f811e | ||
![]() |
4bc4cb7abe | ||
![]() |
f228493399 | ||
![]() |
201377528c | ||
![]() |
6c97d6998f | ||
![]() |
14eaf53b19 | ||
![]() |
dd4887dad4 | ||
![]() |
3a3788e19d | ||
![]() |
e1d2c0935c | ||
![]() |
6a26519418 | ||
![]() |
8ab3238a78 | ||
![]() |
64a59be2b7 | ||
![]() |
17461ccf7f | ||
![]() |
4b652b69b6 | ||
![]() |
15e83e1844 | ||
![]() |
cb3f063dc6 | ||
![]() |
06bac151c4 | ||
![]() |
1d58966ebc | ||
![]() |
8b551ae8c8 | ||
![]() |
c8cdf3170e | ||
![]() |
546cba760f | ||
![]() |
ea4e86dbdd | ||
![]() |
132636e7f9 | ||
![]() |
d36ceb7e3d | ||
![]() |
5d34a64653 | ||
![]() |
27c3d1d842 | ||
![]() |
4f73302b13 | ||
![]() |
635a37385d | ||
![]() |
8a4d7dfd78 | ||
![]() |
05f9c4580d | ||
![]() |
87e363e4bf | ||
![]() |
7034b74b25 | ||
![]() |
c0c36e50dc | ||
![]() |
9f23c4a5d9 | ||
![]() |
429da4655c | ||
![]() |
7354b526c8 |
329 changed files with 32709 additions and 23785 deletions
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -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
31
README.md
Normal 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 ! !!
|
|
@ -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')
|
||||||
|
|
|
@ -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 ''
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
adhdebit, adhcredit = soldes_adherent(dlinf, dlsup, adherent, verbose)
|
try:
|
||||||
if adhdebit + adhcredit > 0 and adhdebit + adhcredit < 1000000: # On evite Toto Passoir
|
adhdebit, adhcredit = soldes_adherent(dlinf, dlsup, adherent, verbose)
|
||||||
if verbose >= 2:
|
if adhdebit + adhcredit > 0 and adhdebit + adhcredit < 1000000: # On evite Toto Passoir
|
||||||
cprint('-' * 40, 'cyan')
|
if verbose >= 2:
|
||||||
if verbose >= 1:
|
cprint('-' * 40, 'cyan')
|
||||||
cprint('Debit total pour ' + adherent.Nom() + ' : ' + str(adhdebit) + ' euros', 'rouge')
|
if verbose >= 1:
|
||||||
cprint('Credit total pour ' + adherent.Nom() + ' : ' + str(adhcredit) + ' euros', 'vert')
|
name = unicode(adherent['prenom'][0]) + u" " + unicode(adherent['nom'][0])
|
||||||
cprint('=' * 40, 'bleu')
|
cprint(u'Debit total pour ' + name + u' : ' + unicode(adhdebit) + u' euros', 'rouge')
|
||||||
totaldebit += adhdebit
|
cprint(u'Credit total pour ' + name + u' : ' + unicode(adhcredit) + u' euros', 'vert')
|
||||||
totalcredit += adhcredit
|
cprint('=' * 40, 'bleu')
|
||||||
|
totaldebit += adhdebit
|
||||||
|
totalcredit += adhcredit
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
if verbose >= 1:
|
if verbose >= 1:
|
||||||
cprint('=' * 80, 'bleu')
|
cprint('=' * 80, 'bleu')
|
||||||
if dlinf == 0:
|
if dlinf == 0:
|
||||||
|
|
683
archive/bcfg2/Tools/Python.py
Normal file
683
archive/bcfg2/Tools/Python.py
Normal 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, _)
|
|
@ -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:
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
50
archive/utils/quota.py
Normal 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}
|
||||||
|
]
|
||||||
|
|
10
bcfg2/Plugins/Python/PythonDefaults.py
Normal file
10
bcfg2/Plugins/Python/PythonDefaults.py
Normal 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"
|
113
bcfg2/Plugins/Python/PythonEnv.py
Normal file
113
bcfg2/Plugins/Python/PythonEnv.py
Normal 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)
|
33
bcfg2/Plugins/Python/PythonFactories.py
Normal file
33
bcfg2/Plugins/Python/PythonFactories.py
Normal 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)
|
221
bcfg2/Plugins/Python/PythonFile.py
Normal file
221
bcfg2/Plugins/Python/PythonFile.py
Normal 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")
|
294
bcfg2/Plugins/Python/PythonPlugin.py
Normal file
294
bcfg2/Plugins/Python/PythonPlugin.py
Normal 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)
|
||||||
|
)
|
73
bcfg2/Plugins/Python/PythonTools.py
Normal file
73
bcfg2/Plugins/Python/PythonTools.py
Normal 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)
|
||||||
|
|
6
bcfg2/Plugins/Python/__init__.py
Normal file
6
bcfg2/Plugins/Python/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#!/usr/bin/env python2.7
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Python plugin initializator for
|
||||||
|
Bcfg2"""
|
||||||
|
|
||||||
|
from .PythonPlugin import Python
|
|
@ -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' % \
|
||||||
|
|
|
@ -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
16
bin/all/quota
Executable 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 = ""
|
||||||
|
}
|
||||||
|
}'
|
20
cranslib/decorators.py
Normal file
20
cranslib/decorators.py
Normal 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
|
||||||
|
|
|
@ -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,24 +25,41 @@ 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
|
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 -*-
|
||||||
|
@ -45,14 +69,19 @@ 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
|
||||||
|
@ -61,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 :
|
||||||
|
@ -70,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):
|
||||||
data = dict()
|
if type(auth_data) == dict:
|
||||||
for (key, value) in auth_data or []:
|
data = auth_data
|
||||||
# Beware: les valeurs scalaires sont entre guillemets
|
else:
|
||||||
# Ex: Calling-Station-Id: "une_adresse_mac"
|
data = dict()
|
||||||
data[key] = value.replace('"', '')
|
for (key, value) in auth_data or []:
|
||||||
|
# Beware: les valeurs scalaires sont entre guillemets
|
||||||
|
# Ex: Calling-Station-Id: "une_adresse_mac"
|
||||||
|
data[key] = value.replace('"', '')
|
||||||
try:
|
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
|
||||||
|
@ -105,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
|
||||||
|
@ -139,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):
|
||||||
|
@ -165,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:
|
||||||
|
@ -183,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:
|
||||||
|
@ -191,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:
|
||||||
|
@ -234,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
|
||||||
|
@ -252,17 +300,17 @@ 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():
|
||||||
|
@ -274,8 +322,7 @@ def authorize_wifi(data):
|
||||||
|
|
||||||
|
|
||||||
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')
|
||||||
|
@ -290,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.
|
||||||
|
@ -313,7 +467,6 @@ 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]
|
||||||
|
@ -343,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]
|
||||||
|
@ -394,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'
|
||||||
|
@ -428,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
|
||||||
|
@ -460,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
|
|
||||||
|
|
31
freeradius/dynamic_clients.conf
Normal file
31
freeradius/dynamic_clients.conf
Normal 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
|
||||||
|
}
|
||||||
|
|
1
freeradius/modules/rlm_python_unifie.conf
Symbolic link
1
freeradius/modules/rlm_python_unifie.conf
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../rlm_python_unifie.conf
|
|
@ -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
|
|
||||||
}
|
|
38
freeradius/rlm_python_unifie.conf
Normal file
38
freeradius/rlm_python_unifie.conf
Normal 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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
17
freeradius/sites-available/dynamic_clients
Normal file
17
freeradius/sites-available/dynamic_clients
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
20
freeradius/sites-available/filaire
Normal file
20
freeradius/sites-available/filaire
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
######################################################################
|
||||||
|
#
|
||||||
|
# Authentification filaire du crans
|
||||||
|
#
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
server filaire {
|
||||||
|
authorize{
|
||||||
|
preprocess
|
||||||
|
crans_unifie
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticate{
|
||||||
|
crans_unifie
|
||||||
|
}
|
||||||
|
|
||||||
|
post-auth{
|
||||||
|
crans_unifie
|
||||||
|
}
|
||||||
|
}
|
397
freeradius/sites-available/inner-tunnel
Normal file
397
freeradius/sites-available/inner-tunnel
Normal 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
|
466
freeradius/sites-available/wifi
Normal file
466
freeradius/sites-available/wifi
Normal 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
1
freeradius/testing/auth.py
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../auth.py
|
|
@ -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.
|
|
@ -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
16
freeradius/testing/test_inner
Executable 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
|
|
@ -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'
|
||||||
|
@ -90,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
|
||||||
|
@ -278,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
|
||||||
|
@ -576,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())
|
||||||
|
|
|
@ -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:
|
||||||
|
@ -56,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
|
||||||
|
@ -77,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:
|
||||||
|
@ -89,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:
|
||||||
|
@ -107,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',
|
||||||
|
@ -154,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',
|
||||||
|
@ -287,16 +271,36 @@ 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',
|
||||||
'minigiga.adm.crans.org',
|
'minigiga.adm.crans.org',
|
||||||
'batv-0.adm.crans.org',
|
'batb-5.crans.org',
|
||||||
]
|
]
|
||||||
_HIDDEN_SWITCHES=[
|
_HIDDEN_SWITCHES = [
|
||||||
'batp-4.adm.crans.org',
|
'batp-4.adm.crans.org',
|
||||||
'batv-0.adm.crans.org',
|
'batv-0.adm.crans.org',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def guess_switch_fqdn(switch_name):
|
||||||
|
"""Retourne le FQDN d'un switch à partir de son nom"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return socket.gethostbyname_ex(switch_name)[0]
|
||||||
|
except socket.gaierror:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
return socket.gethostbyname_ex(switch_name + ".adm.crans.org")[0]
|
||||||
|
except socket.gaierror:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
return socket.gethostbyname_ex(switch_name + ".crans.org")[0]
|
||||||
|
except socket.gaierror:
|
||||||
|
pass
|
||||||
|
|
||||||
|
raise socket.gaierror
|
||||||
|
|
||||||
def all_switchs(bat=None, hide=_SPECIAL_SWITCHES + _HIDDEN_SWITCHES):
|
def all_switchs(bat=None, hide=_SPECIAL_SWITCHES + _HIDDEN_SWITCHES):
|
||||||
"""Retourne la liste des switchs pour un batiment.
|
"""Retourne la liste des switchs pour un batiment.
|
||||||
|
@ -314,7 +318,12 @@ def all_switchs(bat=None, hide=_SPECIAL_SWITCHES + _HIDDEN_SWITCHES):
|
||||||
for b in bat:
|
for b in bat:
|
||||||
indexes = set(n/100 for n in uplink_prises[b])
|
indexes = set(n/100 for n in uplink_prises[b])
|
||||||
for i in indexes:
|
for i in indexes:
|
||||||
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é
|
||||||
|
|
|
@ -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'
|
||||||
|
|
||||||
|
|
|
@ -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,11 +155,11 @@ To: %s
|
||||||
Subject: Tentative de changement de mot de passe !
|
Subject: Tentative de changement de mot de passe !
|
||||||
|
|
||||||
Tentative de changement du mot de passe de %s par %s.
|
Tentative de changement du mot de passe de %s par %s.
|
||||||
""" % (From, To , login.encode(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:
|
||||||
conn.sendmail(From, To , mail )
|
conn.sendmail(From, To, mail)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# On peut modifier le MDP
|
# On peut modifier le MDP
|
||||||
|
@ -167,54 +167,63 @@ 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,
|
config.password.min_upp,
|
||||||
config.password.min_upp,
|
config.password.min_oth,
|
||||||
config.password.min_oth,
|
config.password.cif_value,
|
||||||
config.password.cif_value,
|
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_cif,
|
||||||
config.password.min_len,
|
config.password.min_low,
|
||||||
config.password.min_cif,
|
config.password.min_upp,
|
||||||
config.password.min_low,
|
config.password.min_oth
|
||||||
config.password.min_upp,
|
),
|
||||||
config.password.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="Affiche ce message et quitte.",
|
'--help',
|
||||||
action="store_true")
|
help="Affiche ce message et quitte.",
|
||||||
parser.add_argument('-n', '--no-cracklib',
|
action="store_true",
|
||||||
help="Permet de contourner les règles de choix du mot de passe" +
|
)
|
||||||
"(réservé aux nounous).",
|
parser.add_argument('-n',
|
||||||
action="store_true")
|
'--no-cracklib',
|
||||||
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="?",
|
parser.add_argument('-v',
|
||||||
help="L'utilisateur dont on veut changer le mot de passe.")
|
'--verbose',
|
||||||
|
help="Permet de contourner les règles de choix du mot de passe" +
|
||||||
|
"(réservé aux nounous).",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
parser.add_argument('login',
|
||||||
|
type=str,
|
||||||
|
nargs="?",
|
||||||
|
help="L'utilisateur dont on veut changer le mot de passe.",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
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))
|
||||||
|
|
|
@ -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."
|
||||||
|
|
|
@ -4,3 +4,4 @@
|
||||||
from config import *
|
from config import *
|
||||||
from encoding import *
|
from encoding import *
|
||||||
|
|
||||||
|
import dns
|
||||||
|
|
|
@ -9,42 +9,35 @@ import datetime
|
||||||
# Fichier généré à partir de bcfg2
|
# Fichier généré à partir de bcfg2
|
||||||
from config_srv import adm_only, role
|
from config_srv import adm_only, role
|
||||||
|
|
||||||
|
# Valeur par défaut pour les champs d'études
|
||||||
|
etudes_defaults = [
|
||||||
|
u"Établissement inconnu",
|
||||||
|
u"Année inconnue",
|
||||||
|
u"Domaine d'études inconnu"
|
||||||
|
]
|
||||||
|
|
||||||
gtfepoch = "19700101000000Z"
|
gtfepoch = "19700101000000Z"
|
||||||
##### Gestion des câblages
|
##### Gestion des câblages
|
||||||
# Selon la date, on met :
|
# Selon la date, on met :
|
||||||
# -ann_scol : Année scolaire en cours
|
|
||||||
# -periode_transitoire : on accepte ceux qui ont payé l'année dernière
|
# -periode_transitoire : on accepte ceux qui ont payé l'année dernière
|
||||||
|
|
||||||
# Ne modifier que les dates !
|
# On récupère l'année scolaire à tout besoin
|
||||||
dat = time.localtime()
|
__annee = time.localtime()[0]
|
||||||
if dat[1] < 8 or dat[1] == 8 and dat[2] < 16:
|
|
||||||
# Si pas encore début août, on est dans l'année précédente
|
|
||||||
ann_scol = dat[0]-1
|
|
||||||
periode_transitoire = False
|
|
||||||
# sinon on change d'année
|
|
||||||
elif dat[1] < 10:
|
|
||||||
# Si pas encore octobre, les gens ayant payé l'année précédente sont
|
|
||||||
# acceptés
|
|
||||||
ann_scol = dat[0]
|
|
||||||
periode_transitoire = True
|
|
||||||
else:
|
|
||||||
# Seulement ceux qui ont payé cette année sont acceptés
|
|
||||||
ann_scol = dat[0]
|
|
||||||
periode_transitoire = False
|
|
||||||
|
|
||||||
debut_periode_transitoire = time.mktime(time.strptime("%s/08/16 00:00:00" % (ann_scol,), "%Y/%m/%d %H:%M:%S"))
|
# Prochaine période transitoire de l'année version generalizedTimeFormat
|
||||||
fin_periode_transitoire = time.mktime(time.strptime("%s/09/30 23:59:59" % (ann_scol,), "%Y/%m/%d %H:%M:%S"))
|
gtf_debut_periode_transitoire = "%s0816000000+0200" % (__annee,)
|
||||||
|
gtf_fin_periode_transitoire = "%s0930235959+0200" % (__annee,)
|
||||||
|
|
||||||
## Bloquage si carte d'étudiants manquante pour l'année en cours
|
# Version timestampées timezone-naïves
|
||||||
# /!\ Par sécurité, ces valeurs sont considérées comme False si
|
debut_periode_transitoire = time.mktime(time.strptime("%s/08/16 00:00:00" % (__annee,), "%Y/%m/%d %H:%M:%S"))
|
||||||
# periode_transitoire est True
|
fin_periode_transitoire = time.mktime(time.strptime("%s/09/30 23:59:59" % (__annee,), "%Y/%m/%d %H:%M:%S"))
|
||||||
# Soft (au niveau du Squid)
|
|
||||||
bl_carte_et_actif = not (dat[1] in [9, 10] or dat[1] == 11 and dat[2] < 7)
|
|
||||||
# Hard (l'adhérent est considéré comme paiement pas ok)
|
|
||||||
bl_carte_et_definitif = not(dat[1] == 11 and dat[2] < 17)
|
|
||||||
|
|
||||||
#Sursis pour les inscription après le 1/11 pour fournir la carte étudiant
|
# On est en période transitoire si on est dans le bon intervale
|
||||||
sursis_carte=8*24*3600
|
periode_transitoire = (debut_periode_transitoire <= time.time() <= fin_periode_transitoire)
|
||||||
|
|
||||||
|
ann_scol = __annee
|
||||||
|
if time.time() <= debut_periode_transitoire:
|
||||||
|
ann_scol -= 1
|
||||||
|
|
||||||
# Gel des cableurs pas a jour de cotisation
|
# Gel des cableurs pas a jour de cotisation
|
||||||
# Les droits ne sont pas retires mais il n'y a plus de sudo
|
# Les droits ne sont pas retires mais il n'y a plus de sudo
|
||||||
|
@ -62,95 +55,210 @@ quota_hard = 10000000
|
||||||
fquota_soft = 0
|
fquota_soft = 0
|
||||||
fquota_hard = 0
|
fquota_hard = 0
|
||||||
# Shell
|
# Shell
|
||||||
login_shell='/bin/zsh'
|
login_shell = '/bin/zsh'
|
||||||
club_login_shell='/usr/bin/rssh'
|
club_login_shell = '/usr/bin/rssh'
|
||||||
# Longueur maximale d'un login
|
# Longueur maximale d'un login
|
||||||
maxlen_login=25
|
maxlen_login = 25
|
||||||
|
|
||||||
shells_possibles = [u'/bin/csh',
|
shells_possibles = [
|
||||||
u'/bin/sh', # tout caca
|
u'/bin/csh',
|
||||||
u'/bin/dash', # un bash light
|
u'/bin/sh', # tout caca
|
||||||
u'/usr/bin/rc',
|
u'/bin/dash', # un bash light
|
||||||
u'/usr/bin/ksh', # symlink vers zsh
|
u'/usr/bin/rc',
|
||||||
u'/bin/ksh', # symlink vers zsh
|
u'/usr/bin/ksh', # symlink vers zsh
|
||||||
u'/usr/bin/tcsh', # TENEX C Shell (csh++)
|
u'/bin/ksh', # symlink vers zsh
|
||||||
u'/bin/tcsh', # TENEX C Shell (csh++)
|
u'/usr/bin/tcsh', # TENEX C Shell (csh++)
|
||||||
u'/bin/bash', # the Bourne-Again SHell
|
u'/bin/tcsh', # TENEX C Shell (csh++)
|
||||||
u'/bin/zsh', # the Z shell
|
u'/bin/bash', # the Bourne-Again SHell
|
||||||
u'/usr/bin/zsh', # the Z shell
|
u'/bin/zsh', # the Z shell
|
||||||
u'/usr/bin/screen',
|
u'/usr/bin/zsh', # the Z shell
|
||||||
u'/bin/rbash', # Bash restreint
|
u'/usr/bin/screen',
|
||||||
u'/usr/bin/rssh', # restricted secure shell allowing only scp and/or sftp
|
u'/bin/rbash', # Bash restreint
|
||||||
u'/usr/local/bin/badPassSh', # demande de changer de mot de passe
|
u'/usr/bin/rssh', # restricted secure shell allowing only scp and/or sftp
|
||||||
u'/usr/bin/passwd', # idem
|
u'/usr/local/bin/badPassSh', # demande de changer de mot de passe
|
||||||
u'/usr/local/bin/disconnect_shell', # déconnexion crans
|
u'/usr/bin/passwd', # idem
|
||||||
u'/usr/scripts/surveillance/disconnect_shell', # idem
|
u'/usr/local/bin/disconnect_shell', # déconnexion crans
|
||||||
u'/usr/sbin/nologin', # This account is currently not available.
|
u'/usr/scripts/surveillance/disconnect_shell', # idem
|
||||||
u'/bin/false', # vraiement méchant
|
u'/usr/sbin/nologin', # This account is currently not available.
|
||||||
u'/usr/bin/es', # n'exsite plus
|
u'/bin/false', # vraiement méchant
|
||||||
u'/usr/bin/esh', # n'existe plus
|
u'/usr/bin/es', # n'exsite plus
|
||||||
u'', # le shell vide pour pouvoir les punis
|
u'/usr/bin/esh', # n'existe plus
|
||||||
|
u'', # le shell vide pour pouvoir les punis
|
||||||
|
]
|
||||||
|
|
||||||
|
shells_gest_crans_order = [
|
||||||
|
"zsh",
|
||||||
|
"bash",
|
||||||
|
"tcsh",
|
||||||
|
"screen",
|
||||||
|
"rbash",
|
||||||
|
"rssh",
|
||||||
|
"badPassSh",
|
||||||
|
"disconnect_shell"
|
||||||
]
|
]
|
||||||
|
|
||||||
shells_gest_crans_order = ["zsh", "bash", "tcsh", "screen", "rbash", "rssh",
|
|
||||||
"badPassSh", "disconnect_shell"]
|
|
||||||
shells_gest_crans = {
|
shells_gest_crans = {
|
||||||
"zsh": {"path":"/bin/zsh", "desc":"Le Z SHell, shell par defaut sur zamok"},
|
"zsh" : {
|
||||||
"bash": {"path":"/bin/bash", "desc":"Le Boune-Again SHell, shell par defaut de la plupart des linux"},
|
"path" : "/bin/zsh",
|
||||||
"tcsh": {"path":"/bin/tcsh", "desc":"C SHell ++"},
|
"desc" : "Le Z SHell, shell par defaut sur zamok"
|
||||||
"screen":{"path":'/usr/bin/screen', "desc":"Un gestionnaire de fenêtre dans un terminal"},
|
},
|
||||||
"rbash": {"path":"/bin/rbash", "desc":"Un bash très restreint, voir man rbash"},
|
"bash" : {
|
||||||
"rssh": {"path":"/usr/bin/rssh", "desc":"Shell ne permetant que les transferts de fichiers via scp ou sftp"},
|
"path" : "/bin/bash",
|
||||||
"badPassSh":{"path":"/usr/local/bin/badPassSh", "desc":"Demande de changer de mot de passe à la connexion"},
|
"desc" : "Le Boune-Again SHell, shell par defaut de la plupart des linux"
|
||||||
"disconnect_shell":{"path":"/usr/local/bin/disconnect_shell", "desc":"Shell pour les suspensions de compte avec message explicatif"},
|
},
|
||||||
|
"tcsh" : {
|
||||||
|
"path" : "/bin/tcsh",
|
||||||
|
"desc" : "C SHell ++"
|
||||||
|
},
|
||||||
|
"screen" : {
|
||||||
|
"path" : '/usr/bin/screen',
|
||||||
|
"desc" : "Un gestionnaire de fenêtre dans un terminal"
|
||||||
|
},
|
||||||
|
"rbash" : {
|
||||||
|
"path" : "/bin/rbash",
|
||||||
|
"desc" : "Un bash très restreint, voir man rbash"
|
||||||
|
},
|
||||||
|
"rssh" : {
|
||||||
|
"path" : "/usr/bin/rssh",
|
||||||
|
"desc" : "Shell ne permetant que les transferts de fichiers via scp ou sftp"
|
||||||
|
},
|
||||||
|
"badPassSh" : {
|
||||||
|
"path" : "/usr/local/bin/badPassSh",
|
||||||
|
"desc" : "Demande de changer de mot de passe à la connexion"
|
||||||
|
},
|
||||||
|
"disconnect_shell" : {
|
||||||
|
"path" : "/usr/local/bin/disconnect_shell",
|
||||||
|
"desc" : "Shell pour les suspensions de compte avec message explicatif"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
# Quels droits donnent l'appartenance à quel groupe Unix ?
|
# Quels droits donnent l'appartenance à quel groupe Unix ?
|
||||||
droits_groupes = {'adm' : [u'Nounou'],
|
droits_groupes = {
|
||||||
'respbats' : [u'Imprimeur', u'Cableur', u'Nounou'],
|
'adm' : [
|
||||||
'apprentis' : [u'Apprenti'],
|
u'Nounou',
|
||||||
'moderateurs' : [u'Moderateur'],
|
],
|
||||||
'disconnect' : [u'Bureau'],
|
'respbats' : [
|
||||||
'imprimeurs' : [u'Imprimeur', u'Nounou', u'Tresorier'],
|
u'Imprimeur',
|
||||||
'bureau' : [u'Bureau'],
|
u'Cableur',
|
||||||
'webadm' : [u'Webmaster'],
|
u'Nounou',
|
||||||
'webradio' : [u'Webradio'],
|
],
|
||||||
}
|
'apprentis' : [
|
||||||
|
u'Apprenti',
|
||||||
|
],
|
||||||
|
'moderateurs' : [
|
||||||
|
u'Moderateur',
|
||||||
|
],
|
||||||
|
'disconnect' : [
|
||||||
|
u'Bureau',
|
||||||
|
],
|
||||||
|
'imprimeurs' : [
|
||||||
|
u'Imprimeur',
|
||||||
|
u'Nounou',
|
||||||
|
u'Tresorier',
|
||||||
|
],
|
||||||
|
'bureau' : [
|
||||||
|
u'Bureau',
|
||||||
|
],
|
||||||
|
'webadm' : [
|
||||||
|
u'Webmaster',
|
||||||
|
],
|
||||||
|
'webradio' : [
|
||||||
|
u'Webradio',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
####### Les modes de paiement accepté par le crans
|
####### Les modes de paiement accepté par le crans
|
||||||
|
|
||||||
modePaiement = ['liquide', 'paypal', 'solde', 'cheque', 'carte']
|
modePaiement = [
|
||||||
|
'liquide',
|
||||||
|
'paypal',
|
||||||
|
'solde',
|
||||||
|
'cheque',
|
||||||
|
'carte',
|
||||||
|
'comnpay',
|
||||||
|
'arbitraire',
|
||||||
|
'note',
|
||||||
|
]
|
||||||
|
|
||||||
####### Les ML
|
####### Les ML
|
||||||
# Le + devant un nom de ML indique une synchronisation
|
# Le + devant un nom de ML indique une synchronisation
|
||||||
# ML <-> fonction partielle : il n'y a pas d'effacement automatique
|
# ML <-> fonction partielle : il n'y a pas d'effacement automatique
|
||||||
# des abonnés si le droit est retiré
|
# des abonnés si le droit est retiré
|
||||||
droits_mailing_listes = {'roots' : [ u'Nounou', u'Apprenti'],
|
droits_mailing_listes = {
|
||||||
'mailman' : [ u'Nounou'],
|
'roots' : [
|
||||||
'+nounou' : [ u'Nounou', u'Apprenti'],
|
u'Nounou',
|
||||||
'respbats' : [ u'Cableur', u'Nounou', u'Bureau'],
|
u'Apprenti',
|
||||||
'moderateurs' : [ u'Moderateur', u'Bureau'],
|
],
|
||||||
'disconnect' : [ u'Nounou', u'Bureau'],
|
'mailman' : [
|
||||||
'impression' : [ u'Imprimeur'],
|
u'Nounou',
|
||||||
'bureau' : [u'Bureau'],
|
],
|
||||||
'tresorier' : [u'Tresorier'],
|
'+nounou' : [
|
||||||
'apprentis' : [u'Apprenti'],
|
u'Nounou',
|
||||||
'+ca' : [u'Bureau', u'Apprenti', u'Nounou', u'Cableur'],
|
u'Apprenti',
|
||||||
|
],
|
||||||
|
'respbats' : [
|
||||||
|
u'Cableur',
|
||||||
|
u'Nounou',
|
||||||
|
u'Bureau',
|
||||||
|
],
|
||||||
|
'moderateurs' : [
|
||||||
|
u'Moderateur',
|
||||||
|
u'Bureau',
|
||||||
|
],
|
||||||
|
'disconnect' : [
|
||||||
|
u'Nounou',
|
||||||
|
u'Bureau',
|
||||||
|
],
|
||||||
|
'impression' : [
|
||||||
|
u'Imprimeur',
|
||||||
|
],
|
||||||
|
'bureau' : [
|
||||||
|
u'Bureau',
|
||||||
|
],
|
||||||
|
'tresorier' : [
|
||||||
|
u'Tresorier',
|
||||||
|
],
|
||||||
|
'apprentis' : [
|
||||||
|
u'Apprenti',
|
||||||
|
],
|
||||||
|
'+ca' : [
|
||||||
|
u'Bureau',
|
||||||
|
u'Apprenti',
|
||||||
|
u'Nounou',
|
||||||
|
u'Cableur',
|
||||||
|
],
|
||||||
|
'+federez' : [
|
||||||
|
u'Bureau',
|
||||||
|
u'Apprenti',
|
||||||
|
u'Nounou',
|
||||||
|
],
|
||||||
|
'+install-party' : [
|
||||||
|
u'Bureau',
|
||||||
|
u'Apprenti',
|
||||||
|
u'Nounou',
|
||||||
|
],
|
||||||
|
|
||||||
'+federez' : [u'Bureau', u'Apprenti', u'Nounou'],
|
# Correspondance partielle nécessaire... Des adresses non-crans sont inscrites à ces ML.
|
||||||
'+install-party' : [u'Bureau', u'Apprenti', u'Nounou'],
|
'+dsi-crans' : [
|
||||||
|
u'Nounou',
|
||||||
# Correspondance partielle nécessaire... Des adresses non-crans sont inscrites à ces ML.
|
u'Bureau',
|
||||||
'+dsi-crans' : [u'Nounou', u'Bureau'],
|
],
|
||||||
'+crous-crans' : [u'Nounou', u'Bureau'],
|
'+crous-crans' : [
|
||||||
|
u'Nounou',
|
||||||
'+wrc' : [u'Webradio'],
|
u'Bureau',
|
||||||
}
|
],
|
||||||
|
'+wrc' : [
|
||||||
|
u'Webradio',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
#: Répertoire de stockage des objets détruits
|
#: Répertoire de stockage des objets détruits
|
||||||
cimetiere = '/home/cimetiere'
|
cimetiere = '/home/cimetiere'
|
||||||
|
|
||||||
#: Adresses mac utiles
|
#: Adresses mac utiles
|
||||||
mac_komaz = 'a0:d3:c1:00:f4:04'
|
# Mac du routeur est la mac du routeur du crans (actuellement odlyd)
|
||||||
|
# Utilisé par ra2.py, à changer si le routeur est remplacé
|
||||||
|
mac_komaz = '00:19:bb:31:3b:80'
|
||||||
|
mac_du_routeur = 'a0:d3:c1:00:f4:04'
|
||||||
mac_titanic = 'aa:73:65:63:6f:76'
|
mac_titanic = 'aa:73:65:63:6f:76'
|
||||||
|
|
||||||
#: Serveur principal de bcfg2
|
#: Serveur principal de bcfg2
|
||||||
|
@ -165,21 +273,21 @@ ISCSI_MAP_FILE = "/usr/scripts/var/iscsi_names_%s.py"
|
||||||
# IANA_id correspond à l'entier attribué par l'IANA pour l'algorithm dans les champs DNS SSHFP
|
# IANA_id correspond à l'entier attribué par l'IANA pour l'algorithm dans les champs DNS SSHFP
|
||||||
# ssh_algo correspond a la première chaine de caractères donnant le nom de l'algorithme de chiffrement lorsque la clef ssh est dans le format openssh (algo key comment)
|
# ssh_algo correspond a la première chaine de caractères donnant le nom de l'algorithme de chiffrement lorsque la clef ssh est dans le format openssh (algo key comment)
|
||||||
sshfp_algo = {
|
sshfp_algo = {
|
||||||
"rsa" : (1, "ssh-rsa"),
|
"rsa" : (1, "ssh-rsa"),
|
||||||
"dsa" : (2, "ssh-dss"),
|
"dsa" : (2, "ssh-dss"),
|
||||||
"ecdsa-256" : (3, "ecdsa-sha2-nistp256"),
|
"ecdsa-256" : (3, "ecdsa-sha2-nistp256"),
|
||||||
"ecdsa-384" : (3, "ecdsa-sha2-nistp384"),
|
"ecdsa-384" : (3, "ecdsa-sha2-nistp384"),
|
||||||
"ecdsa-521" : (3, "ecdsa-sha2-nistp521"),
|
"ecdsa-521" : (3, "ecdsa-sha2-nistp521"),
|
||||||
"ecdsa" : (3, "ecdsa-sha2-nistp521"),
|
"ecdsa" : (3, "ecdsa-sha2-nistp521"),
|
||||||
}
|
}
|
||||||
|
|
||||||
sshfs_ralgo = {}
|
sshfs_ralgo = {}
|
||||||
for key,value in sshfp_algo.items():
|
for key, value in sshfp_algo.items():
|
||||||
sshfs_ralgo[value[1]] = (value[0], key)
|
sshfs_ralgo[value[1]] = (value[0], key)
|
||||||
|
|
||||||
sshfp_hash = {
|
sshfp_hash = {
|
||||||
"sha1" : 1,
|
"sha1" : 1,
|
||||||
"sha256" : 2,
|
"sha256" : 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
sshkey_max_age = int(9.869604401089358 * (365.25 * 24 * 3600))
|
sshkey_max_age = int(9.869604401089358 * (365.25 * 24 * 3600))
|
||||||
|
@ -217,46 +325,90 @@ plage_ens = '138.231.0.0/16'
|
||||||
# clefs qui cassent la bijectivité, mais qui peuvent servir.
|
# clefs qui cassent la bijectivité, mais qui peuvent servir.
|
||||||
# NETs est l'union des deux
|
# NETs est l'union des deux
|
||||||
NETs_primaires = {
|
NETs_primaires = {
|
||||||
'serveurs' : ['138.231.136.0/24'],
|
'serveurs' : [
|
||||||
'adherents' : ['138.231.137.0/24', '138.231.138.0/23', '138.231.140.0/22'],
|
'138.231.136.0/24',
|
||||||
'wifi-adh' : ['138.231.144.0/22', '138.231.148.128/25', '138.231.149.0/24', '138.231.150.0/23'],
|
],
|
||||||
'bornes' : ['138.231.148.0/25'],
|
'adherents' : [
|
||||||
'adm' : ['10.231.136.0/24'],
|
'138.231.137.0/24',
|
||||||
'personnel-ens' : ['10.2.9.0/24'],
|
'138.231.138.0/23',
|
||||||
'gratuit' : ['10.42.0.0/16'],
|
'138.231.140.0/22',
|
||||||
'accueil' : ['10.51.0.0/16'],
|
],
|
||||||
'isolement' : ['10.52.0.0/16'],
|
'wifi-adh' : [
|
||||||
'evenementiel' : ['10.231.137.0/24'],
|
'138.231.144.0/22',
|
||||||
'multicast' : ['239.0.0.0/8'],
|
'138.231.148.32/27',
|
||||||
'ens' : ['138.231.135.0/24'],
|
'138.231.148.64/26',
|
||||||
}
|
'138.231.148.128/25',
|
||||||
|
'138.231.149.0/24',
|
||||||
|
'138.231.150.0/23',
|
||||||
|
],
|
||||||
|
'bornes' : [
|
||||||
|
'138.231.148.0/27',
|
||||||
|
],
|
||||||
|
'adm' : [
|
||||||
|
'10.231.136.0/24'
|
||||||
|
],
|
||||||
|
'personnel-ens' : [
|
||||||
|
'10.2.9.0/24'
|
||||||
|
],
|
||||||
|
'gratuit' : [
|
||||||
|
'10.42.0.0/16'
|
||||||
|
],
|
||||||
|
'accueil' : [
|
||||||
|
'10.51.0.0/16'
|
||||||
|
],
|
||||||
|
'federez' : [
|
||||||
|
'10.53.0.0/16'
|
||||||
|
],
|
||||||
|
'isolement' : [
|
||||||
|
'10.52.0.0/16'
|
||||||
|
],
|
||||||
|
'evenementiel' : [
|
||||||
|
'10.231.137.0/24'
|
||||||
|
],
|
||||||
|
'multicast' : [
|
||||||
|
'239.0.0.0/8'
|
||||||
|
],
|
||||||
|
'ens' : [
|
||||||
|
'138.231.135.0/24'
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
NETs_secondaires = {
|
NETs_secondaires = {
|
||||||
'all' : ['138.231.136.0/21', '138.231.144.0/21'],
|
'all' : [
|
||||||
'wifi': ['138.231.144.0/21'],
|
'138.231.136.0/21',
|
||||||
'fil' : ['138.231.136.0/21'],
|
'138.231.144.0/21',
|
||||||
}
|
],
|
||||||
|
'wifi': [
|
||||||
|
'138.231.144.0/21',
|
||||||
|
],
|
||||||
|
'fil' : [
|
||||||
|
'138.231.136.0/21',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
NETs = {}
|
NETs = {}
|
||||||
NETs.update(NETs_primaires)
|
NETs.update(NETs_primaires)
|
||||||
NETs.update(NETs_secondaires)
|
NETs.update(NETs_secondaires)
|
||||||
|
|
||||||
NETs_regexp = { 'all' : '^138\.231\.1(3[6789]|4[0123456789]|5[01])\.\d+$' }
|
NETs_regexp = {
|
||||||
|
'all' : r'^138\.231\.1(3[6789]|4[0123456789]|5[01])\.\d+$'
|
||||||
|
}
|
||||||
|
|
||||||
# Classes de rid
|
# Classes de rid
|
||||||
# Merci d'essayer de les faire correspondre avec les réseaux
|
# Merci d'essayer de les faire correspondre avec les réseaux
|
||||||
# ci-dessus...
|
# ci-dessus...
|
||||||
# De même que pout NETs, primaires c'est pour la bijectivité, et secondaires
|
# De même que pout NETs, primaires c'est pour la bijectivité, et secondaires
|
||||||
# pour les trucs pratiques
|
# pour les trucs pratiques
|
||||||
|
# https://wiki.crans.org/CransTechnique/PlanAdressage#Machines
|
||||||
rid_primaires = {
|
rid_primaires = {
|
||||||
# Rid pour les serveurs
|
# Rid pour les serveurs
|
||||||
'serveurs' : [(0, 255),],
|
'serveurs' : [(0, 255),],
|
||||||
# Rid pour les machines fixes
|
# Rid pour les machines fixes
|
||||||
'adherents' : [(256, 2047),],
|
'adherents' : [(256, 2047),],
|
||||||
# Rid pour les machines wifi
|
# Rid pour les machines wifi
|
||||||
'wifi-adh' : [(2048, 3071), (3200, 4095),],
|
'wifi-adh' : [(2048, 3071), (3104, 4095),],
|
||||||
# Rid pour les bornes
|
# Rid pour les bornes
|
||||||
'bornes' : [(3072, 3199),],
|
'bornes' : [(3072, 3103), (34816, 35071),],
|
||||||
# Rid pour machines spéciales
|
# Rid pour machines spéciales
|
||||||
'special' : [(4096, 6143),],
|
'special' : [(4096, 6143),],
|
||||||
# Rid pour les serveurs v6-only
|
# Rid pour les serveurs v6-only
|
||||||
|
@ -265,8 +417,8 @@ rid_primaires = {
|
||||||
'adherents-v6' : [(16384, 24575),],
|
'adherents-v6' : [(16384, 24575),],
|
||||||
# Rid pour les wifi v6-only
|
# Rid pour les wifi v6-only
|
||||||
'wifi-adh-v6' : [(24576, 32767),],
|
'wifi-adh-v6' : [(24576, 32767),],
|
||||||
# Bornes-v6 ?
|
# Bornes-v6
|
||||||
'bornes-v6' : [(32768, 33791),],
|
'bornes-v6' : [(34816, 35071),],
|
||||||
# Rid pour les machines du vlan adm
|
# Rid pour les machines du vlan adm
|
||||||
'adm-v6' : [(49152, 51199),],
|
'adm-v6' : [(49152, 51199),],
|
||||||
# Rid pour les machines du vlan adm
|
# Rid pour les machines du vlan adm
|
||||||
|
@ -277,13 +429,13 @@ rid_primaires = {
|
||||||
'personnel-ens' : [(55296, 55551),],
|
'personnel-ens' : [(55296, 55551),],
|
||||||
# Un unique rid pour les machines multicast
|
# Un unique rid pour les machines multicast
|
||||||
'multicast' : [(65535, 65535),],
|
'multicast' : [(65535, 65535),],
|
||||||
}
|
}
|
||||||
|
|
||||||
rid_secondaires = {
|
rid_secondaires = {
|
||||||
# Rid pour les machines filaire ipv4
|
# Rid pour les machines filaire ipv4
|
||||||
'fil' : [(0, 2047),],
|
'fil' : [(0, 2047),],
|
||||||
'wifi' : [(2048, 4095),],
|
'wifi' : [(2048, 4095), (34816, 35071),],
|
||||||
}
|
}
|
||||||
|
|
||||||
rid = {}
|
rid = {}
|
||||||
rid.update(rid_primaires)
|
rid.update(rid_primaires)
|
||||||
|
@ -309,24 +461,59 @@ ipv6_machines_speciales = {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Les préfixes ipv6 publics
|
# Les préfixes ipv6 publics
|
||||||
prefix = { 'subnet' : [ '2a01:240:fe3d::/48' ],
|
prefix = {
|
||||||
'serveurs' : [ '2a01:240:fe3d:4::/64' ],
|
'subnet' : [
|
||||||
'adherents' : [ '2a01:240:fe3d:4::/64' ],
|
'2a01:240:fe3d::/48',
|
||||||
'fil' : [ '2a01:240:fe3d:4::/64' ],
|
],
|
||||||
'adm' : [ '2a01:240:fe3d:c804::/64' ],
|
'serveurs' : [
|
||||||
'adm-v6' : [ '2a01:240:fe3d:c804::/64' ],
|
'2a01:240:fe3d:4::/64',
|
||||||
'wifi' : [ '2a01:240:fe3d:c04::/64' ],
|
],
|
||||||
'serveurs-v6' : [ '2a01:240:fe3d:c04::/64' ],
|
'adherents' : [
|
||||||
'adherents-v6' : [ '2a01:240:fe3d:4::/64' ],
|
'2a01:240:fe3d:4::/64',
|
||||||
'wifi-adh-v6' : [ '2a01:240:fe3d:c04::/64' ],
|
],
|
||||||
'personnel-ens' : [ '2a01:240:fe3d:4::/64' ],
|
'fil' : [
|
||||||
'sixxs2' : [ '2a01:240:fe00:68::/64' ],
|
'2a01:240:fe3d:4::/64',
|
||||||
'evenementiel' : [ '2a01:240:fe3d:d2::/64' ],
|
],
|
||||||
'bornes' : [ '2a01:240:fe3d:c04::/64' ],
|
'adm' : [
|
||||||
'bornes-v6' : [ '2a01:240:fe3d:c04::/64' ],
|
'2a01:240:fe3d:c804::/64',
|
||||||
'wifi-adh' : [ '2a01:240:fe3d:c04::/64' ],
|
],
|
||||||
'v6only' : [ '2001:470:c8b9:a4::/64' ],
|
'adm-v6' : [
|
||||||
}
|
'2a01:240:fe3d:c804::/64',
|
||||||
|
],
|
||||||
|
'wifi' : [
|
||||||
|
'2a01:240:fe3d:c04::/64',
|
||||||
|
],
|
||||||
|
'serveurs-v6' : [
|
||||||
|
'2a01:240:fe3d:c04::/64',
|
||||||
|
],
|
||||||
|
'adherents-v6' : [
|
||||||
|
'2a01:240:fe3d:4::/64',
|
||||||
|
],
|
||||||
|
'wifi-adh-v6' : [
|
||||||
|
'2a01:240:fe3d:c04::/64',
|
||||||
|
],
|
||||||
|
'personnel-ens' : [
|
||||||
|
'2a01:240:fe3d:4::/64',
|
||||||
|
],
|
||||||
|
'sixxs2' : [
|
||||||
|
'2a01:240:fe00:68::/64',
|
||||||
|
],
|
||||||
|
'evenementiel' : [
|
||||||
|
'2a01:240:fe3d:d2::/64',
|
||||||
|
],
|
||||||
|
'bornes' : [
|
||||||
|
'2a01:240:fe3d:c04::/64',
|
||||||
|
],
|
||||||
|
'bornes-v6' : [
|
||||||
|
'2a01:240:fe3d:c04::/64',
|
||||||
|
],
|
||||||
|
'wifi-adh' : [
|
||||||
|
'2a01:240:fe3d:c04::/64',
|
||||||
|
],
|
||||||
|
'v6only' : [
|
||||||
|
'2001:470:c8b9:a4::/64',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
# Préfixes ipv6 internes (ula)
|
# Préfixes ipv6 internes (ula)
|
||||||
int_prefix = {
|
int_prefix = {
|
||||||
|
@ -335,10 +522,12 @@ int_prefix = {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Domaines dans lesquels les machines sont placées suivant leur type
|
# Domaines dans lesquels les machines sont placées suivant leur type
|
||||||
domains = { 'machineFixe': 'crans.org',
|
domains = {
|
||||||
'machineCrans': 'crans.org',
|
'machineFixe': 'crans.org',
|
||||||
'machineWifi': 'wifi.crans.org',
|
'machineCrans': 'crans.org',
|
||||||
'borneWifi': 'wifi.crans.org' }
|
'machineWifi': 'wifi.crans.org',
|
||||||
|
'borneWifi': 'wifi.crans.org',
|
||||||
|
}
|
||||||
|
|
||||||
# VLans
|
# VLans
|
||||||
vlans = {
|
vlans = {
|
||||||
|
@ -358,10 +547,10 @@ vlans = {
|
||||||
'v6only': 6,
|
'v6only': 6,
|
||||||
# Vlan isolement
|
# Vlan isolement
|
||||||
'isolement' : 9,
|
'isolement' : 9,
|
||||||
# Vlan de tests de chiffrement DSI
|
|
||||||
'chiffrement': 11,
|
|
||||||
# VLan des appartements de l'ENS
|
# VLan des appartements de l'ENS
|
||||||
'appts': 21,
|
'appts': 21,
|
||||||
|
# Vlan federez-wifi
|
||||||
|
'federez': 22,
|
||||||
# Vlan evenementiel (install-party, etc)
|
# Vlan evenementiel (install-party, etc)
|
||||||
'event': 10,
|
'event': 10,
|
||||||
# Vlan zone routeur ens (zrt)
|
# Vlan zone routeur ens (zrt)
|
||||||
|
@ -370,68 +559,100 @@ vlans = {
|
||||||
'iscsi': 42,
|
'iscsi': 42,
|
||||||
# freebox (pour faire descendre la connexion au 0B)
|
# freebox (pour faire descendre la connexion au 0B)
|
||||||
'freebox': 8,
|
'freebox': 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_policy = { 'komaz' : { 'policy_input' : 'ACCEPT',
|
filter_policy = {
|
||||||
'policy_forward' : 'ACCEPT',
|
'komaz' : {
|
||||||
'policy_output' : 'ACCEPT'
|
'policy_input' : 'ACCEPT',
|
||||||
},
|
'policy_forward' : 'ACCEPT',
|
||||||
'zamok' : { 'policy_input' : 'ACCEPT',
|
'policy_output' : 'ACCEPT',
|
||||||
'policy_forward' : 'DROP',
|
},
|
||||||
'policy_output' : 'ACCEPT'
|
'zamok' : {
|
||||||
},
|
'policy_input' : 'ACCEPT',
|
||||||
'default' : { 'policy_input' : 'ACCEPT',
|
'policy_forward' : 'DROP',
|
||||||
'policy_forward' : 'ACCEPT',
|
'policy_output' : 'ACCEPT',
|
||||||
'policy_output' : 'ACCEPT'
|
},
|
||||||
}
|
'default' : {
|
||||||
}
|
'policy_input' : 'ACCEPT',
|
||||||
|
'policy_forward' : 'ACCEPT',
|
||||||
|
'policy_output' : 'ACCEPT',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Cf RFC 4890
|
# Cf RFC 4890
|
||||||
authorized_icmpv6 = ['echo-request', 'echo-reply', 'destination-unreachable',
|
authorized_icmpv6 = [
|
||||||
'packet-too-big', 'ttl-zero-during-transit', 'parameter-problem']
|
'echo-request',
|
||||||
|
'echo-reply',
|
||||||
|
'destination-unreachable',
|
||||||
|
'packet-too-big',
|
||||||
|
'ttl-zero-during-transit',
|
||||||
|
'parameter-problem',
|
||||||
|
]
|
||||||
|
|
||||||
output_file = { 4 : '/tmp/ipt_rules',
|
output_file = {
|
||||||
6 : '/tmp/ip6t_rules'
|
4 : '/tmp/ipt_rules',
|
||||||
}
|
6 : '/tmp/ip6t_rules',
|
||||||
|
}
|
||||||
|
|
||||||
file_pickle = { 4 : '/tmp/ipt_pickle',
|
file_pickle = {
|
||||||
6 : '/tmp/ip6t_pickle'
|
4 : '/tmp/ipt_pickle',
|
||||||
}
|
6 : '/tmp/ip6t_pickle',
|
||||||
|
}
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
#: Items de la blackliste
|
#: Items de la blackliste
|
||||||
blacklist_items = { u'bloq': u'Blocage total de tous les services',
|
blacklist_items = {
|
||||||
u'carte_etudiant': u'Carte etudiant manquante',
|
u'bloq': u'Blocage total de tous les services',
|
||||||
u'paiement': u'Paiement manquant cette année',
|
u'paiement': u'Paiement manquant cette année',
|
||||||
u'virus': u'Passage en VLAN isolement',
|
u'virus': u'Passage en VLAN isolement',
|
||||||
u'upload': u"Bridage du débit montant vers l'extérieur",
|
u'upload': u"Bridage du débit montant vers l'extérieur",
|
||||||
u'p2p': u"Blocage total de l'accès à l'extérieur",
|
u'autodisc_upload': u'Autodisconnect pour upload',
|
||||||
u'autodisc_virus': u'Autodisconnect pour virus',
|
u'ipv6_ra': u'Isolement pour RA',
|
||||||
u'autodisc_upload': u'Autodisconnect pour upload',
|
u'mail_invalide': u'Blocage pour mail invalide',
|
||||||
u'autodisc_p2p': u'Autodisconnect pour P2P',
|
u'warez' : u"Présence de contenu violant de droit d'auteur sur zamok",
|
||||||
u'ipv6_ra': u'Isolement pour RA',
|
}
|
||||||
u'mail_invalide': u'Blocage pour mail invalide',
|
|
||||||
u'warez' : u"Présence de contenu violant de droit d'auteur sur zamok",
|
|
||||||
}
|
|
||||||
|
|
||||||
#: Blacklistes entrainant une déconnexion complète
|
#: Blacklistes entrainant une déconnexion complète
|
||||||
blacklist_sanctions = ['warez', 'p2p', 'autodisc_p2p','autodisc_virus','virus', 'bloq',
|
blacklist_sanctions = [
|
||||||
|
'warez',
|
||||||
|
'virus',
|
||||||
|
'bloq',
|
||||||
'paiement',
|
'paiement',
|
||||||
]
|
]
|
||||||
if bl_carte_et_definitif:
|
|
||||||
blacklist_sanctions.append('carte_etudiant')
|
|
||||||
#: Blacklistes redirigeant le port 80 en http vers le portail captif (avec des explications)
|
#: Blacklistes redirigeant le port 80 en http vers le portail captif (avec des explications)
|
||||||
blacklist_sanctions_soft = ['autodisc_virus','ipv6_ra','mail_invalide','virus',
|
blacklist_sanctions_soft = [
|
||||||
'warez', 'p2p', 'autodisc_p2p', 'bloq','carte_etudiant','chambre_invalide']
|
'ipv6_ra',
|
||||||
|
'mail_invalide',
|
||||||
|
'virus',
|
||||||
|
'warez',
|
||||||
|
'bloq',
|
||||||
|
'chambre_invalide',
|
||||||
|
]
|
||||||
|
|
||||||
#: Blacklistes entrainant un bridage de la connexion pour upload
|
#: Blacklistes entrainant un bridage de la connexion pour upload
|
||||||
blacklist_bridage_upload = ['autodisc_upload', 'upload']
|
blacklist_bridage_upload = ['autodisc_upload', 'upload']
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
|
|
||||||
adm_users = [ 'root', 'identd', 'daemon', 'postfix', 'freerad', 'amavis',
|
adm_users = [
|
||||||
'nut', 'respbats', 'list', 'sqlgrey', 'ntpd', 'lp' ]
|
'root',
|
||||||
|
'identd',
|
||||||
|
'daemon',
|
||||||
|
'postfix',
|
||||||
|
'freerad',
|
||||||
|
'amavis',
|
||||||
|
'nut',
|
||||||
|
'respbats',
|
||||||
|
'list',
|
||||||
|
'sqlgrey',
|
||||||
|
'ntpd',
|
||||||
|
'lp',
|
||||||
|
]
|
||||||
|
|
||||||
open_ports = { 'tcp' : '22' }
|
open_ports = {
|
||||||
|
'tcp' : '22',
|
||||||
|
}
|
||||||
|
|
||||||
# Debit max sur le vlan de la connexion gratuite
|
# Debit max sur le vlan de la connexion gratuite
|
||||||
debit_max_radin = 1000000
|
debit_max_radin = 1000000
|
||||||
|
@ -441,17 +662,90 @@ debit_max_gratuit = 1000000
|
||||||
## Vlan accueil et isolement ##
|
## Vlan accueil et isolement ##
|
||||||
###############################
|
###############################
|
||||||
accueil_route = {
|
accueil_route = {
|
||||||
'138.231.136.1':{'tcp':['80','443', '22'],'hosts':['intranet.crans.org', 'ssh.crans.org', 'zamok.crans.org']},
|
'138.231.136.1' : {
|
||||||
'138.231.136.67':{'tcp':['80','443'],'hosts':['www.crans.org', 'wiki.crans.org', 'wifi.crans.org']},
|
'tcp' : [
|
||||||
'138.231.136.98':{'tcp':['20','21','80','111','1024:65535'],'udp':['69','1024:65535'], 'hosts':['ftp.crans.org']},
|
'80',
|
||||||
'138.231.136.130':{'tcp':['80','443'],'hosts':['intranet2.crans.org']},
|
'443',
|
||||||
'138.231.136.18':{'tcp':['80','443'],'hosts':['cas.crans.org', 'login.crans.org', 'auth.crans.org']},
|
'22'
|
||||||
'213.154.225.236':{'tcp':['80','443'], 'hosts':['crl.cacert.org']},
|
],
|
||||||
'213.154.225.237':{'tcp':['80','443'], 'hosts':['ocsp.cacert.org']},
|
'hosts' : [
|
||||||
|
'ssh.crans.org',
|
||||||
|
'zamok.crans.org',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'138.231.136.67' : {
|
||||||
|
'tcp' : [
|
||||||
|
'80',
|
||||||
|
'443',
|
||||||
|
],
|
||||||
|
'hosts' : [
|
||||||
|
'www.crans.org',
|
||||||
|
'wiki.crans.org',
|
||||||
|
'wifi.crans.org',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'138.231.136.98' : {
|
||||||
|
'tcp' : [
|
||||||
|
'20',
|
||||||
|
'21',
|
||||||
|
'80',
|
||||||
|
'111',
|
||||||
|
'1024:65535',
|
||||||
|
],
|
||||||
|
'udp' : [
|
||||||
|
'69',
|
||||||
|
'1024:65535',
|
||||||
|
],
|
||||||
|
'hosts' : [
|
||||||
|
'ftp.crans.org',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'138.231.136.130' : {
|
||||||
|
'tcp' : [
|
||||||
|
'80',
|
||||||
|
'443',
|
||||||
|
],
|
||||||
|
'hosts' : [
|
||||||
|
'intranet2.crans.org',
|
||||||
|
'intranet.crans.org',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'138.231.136.18' : {
|
||||||
|
'tcp' : [
|
||||||
|
'80',
|
||||||
|
'443',
|
||||||
|
],
|
||||||
|
'hosts' : [
|
||||||
|
'cas.crans.org',
|
||||||
|
'login.crans.org',
|
||||||
|
'auth.crans.org',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'213.154.225.236' : {
|
||||||
|
'tcp' : [
|
||||||
|
'80',
|
||||||
|
'443',
|
||||||
|
],
|
||||||
|
'hosts' : [
|
||||||
|
'crl.cacert.org',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'213.154.225.237' : {
|
||||||
|
'tcp' : [
|
||||||
|
'80',
|
||||||
|
'443',
|
||||||
|
],
|
||||||
|
'hosts' : [
|
||||||
|
'ocsp.cacert.org',
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
dhcp_servers = ['dhcp.adm.crans.org', 'isc.adm.crans.org']
|
dhcp_servers = ['dhcp.adm.crans.org', 'isc.adm.crans.org']
|
||||||
|
|
||||||
# Le bâtiment virtuel dans lequel on place des chambres qui n'existent pas, pour faire
|
# Le bâtiment virtuel dans lequel on place des chambres qui n'existent pas, pour faire
|
||||||
# des tests.
|
# des tests.
|
||||||
bat_virtuel = 'v'
|
bats_virtuels = ['v']
|
||||||
|
|
||||||
|
# Liste des batiments
|
||||||
|
liste_bats = ['a', 'b', 'c', 'h', 'i', 'j', 'm', 'g', 'p', 'k']
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue