Cas particulier pour le Vietnam (merci Microsoft, encore)…
[auf-refer.git] / auf-annuaire
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """Outil de gestion d'annuaire LDAP pour l'AuF.
4
5 Copyright ©2009-2012  Agence universitaire de la Francophonie
6 Licence : GPL version 3
7 Auteur : Progfou <jean-christophe.andre@auf.org>
8
9 Dépendances Debian : python >= 2.5, auf-refer
10
11 Usage :
12
13   auf-annuaire.py | ldapmodify -x -c -D "cn=admin,o=AUF" -y /etc/ldap.secret
14 """
15 import os
16 import aufrefer
17
18 LDAP_HOST = '127.0.0.1'
19 LDAP_BASE = 'ou=People,o=AUF'
20 LDAP_BINDDN = 'cn=admin,' + LDAP_BASE
21 LDAP_PWFILE = ['~/.ldap.secret', '/etc/ldap.secret']
22
23 LDAP_INIT_LDIF = u"""dn: ou=People,o=AuF
24 changeType: add
25 objectClass: top
26 objectClass: organizationalUnit
27 ou: People
28 """
29
30 ISO_3166 = aufrefer.get('iso-3166-fr.json')
31 NOMS_BUREAU = aufrefer.get('bureaux.json')
32 PAYS_IMPLANT = aufrefer.get('pays-implant.json')
33
34 PHOTO_FILENAME = "/var/lib/auf-refer-photos/%s.jpg"
35
36 def noms_bureau(cc):
37     for code, data in NOMS_BUREAU.items():
38         if cc.upper() in data[1]:
39             return code, data[0]
40     return u'### implantation inconnue ###', u'### implantation inconnue ###'
41
42 class LdapEntry(object):
43     _attributes_order = [
44         'objectClass', 'sn', 'givenName', 'cn', 'ou', 'c', 'mail', 'o',
45         'userPassword', 'telephoneNumber', 'uid',
46     ]
47
48     def __init__(self, dn, d=None, **kv):
49         self.dn = dn
50         if d:
51             self.dict = d
52         else:
53             self.dict = { }
54         self.dict.update(kv)
55
56     def get(self, attribute):
57         return self.dict.get(attribute, None)
58
59     def set(self, attribute, value):
60         old_value = self.dict.get(attribute, None)
61         self.dict[attribute] = value
62         return old_value
63
64     def from_ldif(self, ldif):
65         raise RuntimeError('Note implemented')
66
67     def _to_ldif_line(self, attribute, value):
68         to_encode = False
69         for c in value:
70             if c < ' ' or 'z' < c:
71                 to_encode = True
72                 break
73         if to_encode:
74             attribute = attribute + ':'
75             if type(value) == unicode:
76                 value = value.encode('utf-8')
77             value = value.encode('base64').replace('\n', '')
78         return u'%s: %s' % (attribute, value)
79
80     def to_ldif(self, changeType=None):
81         ldif = [ ]
82         ldif.append(self._to_ldif_line(u'dn', self.dn))
83         if changeType:
84             ldif.append(u'changeType: %s' % changeType)
85         set_keys = set(self.dict.keys())
86         set_attr_order = set(self._attributes_order)
87         for k in set_keys & set_attr_order:
88             for e in self.dict[k]:
89                 ldif.append(self._to_ldif_line(k, e))
90         for k in set_keys - set_attr_order:
91             for e in self.dict[k]:
92                 ldif.append(self._to_ldif_line(k, e))
93         ldif.append(u'')
94         return '\n'.join(ldif).encode('utf-8')
95
96
97 class AnnuaireAuF(object):
98     _attributes = [
99         'adel', 'redir', 'nom', 'mdp', 'implant', 'coda', 'tel_ext', 'tel_ip',
100     ]
101
102     def __init__(self, ldap_base):
103         self.ldap_base = ldap_base
104         self.data = None
105
106     def json2ldap(self, json_dict):
107         implant = json_dict['implant'].upper()
108         CC = PAYS_IMPLANT.get(implant, implant)
109         nom_bureau_court, nom_bureau_long = noms_bureau(CC)
110         adel = json_dict['adel']
111         user, domain = adel.rsplit('@', 1)
112         cn = user.title().replace('-De-', '-de-').replace('.', ' ')
113         if user.count('.') == 1:
114             prenom, nom = cn.rsplit(' ', 1)
115         else:
116             # cas des noms vietnamiens
117             nom, prenom = cn.rsplit(' ', 1)
118         login = json_dict['login']
119         nom_affichage = json_dict['nom'] and json_dict['nom'] or cn
120
121         ldap_dict = { }
122         ldap_dict['objectClass'] = [
123             u'top', u'person', u'organizationalPerson', u'inetOrgPerson',
124             u'inetLocalMailRecipient', u'mozillaAbPersonAlpha',
125         ]
126         ldap_dict['sn'] = [ nom ]
127         ldap_dict['givenName'] = [ prenom ]
128         ldap_dict['cn'] = [ nom_affichage ]
129         ldap_dict['ou'] = [ u'People', nom_bureau_court, nom_bureau_long ]
130         ldap_dict['c'] = [ CC ]
131         ldap_dict['mail'] = [ adel ]
132         ldap_dict['o'] = [ u'AuF', u'Agence universitaire de la Francophonie' ]
133         if json_dict.has_key('mdp') and json_dict['mdp']:
134             ldap_dict['userPassword'] = [ '{CRYPT}' + json_dict['mdp'] ]
135         if json_dict.has_key('tel_ip') and json_dict['tel_ip']:
136             ldap_dict['telephoneNumber'] = [ json_dict['tel_ip'] ]
137         ldap_dict['uid'] = [ user ]
138         if login is not None and login and login != user:
139             ldap_dict['uid'].append(login)
140         # objectClass: inetLocalMailRecipient
141         ldap_dict['mailLocalAddress'] = [ json_dict['redir'] ]
142         ldap_dict['mailHost'] = [ 'imaps://mail.%s:993' % json_dict['redir'].rsplit('@', 1)[1] ]
143         # photo JPEG
144         photo_filename = PHOTO_FILENAME % adel
145         if os.path.exists(photo_filename):
146             ldap_dict['jpegPhoto'] = [ file(photo_filename, 'rb').read() ]
147             #ldap_dict['photo'] = [ file(photo_filename, 'rb').read() ]
148         # compte POSIX
149         if json_dict['employe']:
150             ldap_dict['objectClass'].append(u'posixAccount')
151             ldap_dict['uidNumber'] = [ str(2000 + int(json_dict['employe']['id'])) ]
152             ldap_dict['gidNumber'] = [ str(100) ]
153             ldap_dict['homeDirectory'] = [ '/home/auf/%s' % user ]
154             ldap_dict['loginShell'] = [ '/bin/false' ]
155         # message d'absence
156         if json_dict['vacation']:
157             ldap_dict['description'] = [ json_dict['vacation']['message'] ]
158             debut = json_dict['vacation']['debut']
159             debut = '%s/%s/%s' % (debut[8:10], debut[5:7], debut[0:4])
160             fin = json_dict['vacation']['fin']
161             fin = '%s/%s/%s' % (fin[8:10], fin[5:7], fin[0:4])
162             ldap_dict['l'] = [ u'Absence du %s au %s' % (debut, fin) ]
163         # identification unique
164         ldap_dn = u'mail=%s,%s' % (adel, self.ldap_base)
165         return ldap_dn, ldap_dict
166
167     def load(self):
168         self.data = [ ]
169         employes = aufrefer.get('datamaster-employe.json')
170         employes = dict([(e['courriel'],e) for e in employes if e['courriel']])
171         vacation = aufrefer.get('vacation.json')
172         vacation = dict([(v['adel'],v) for v in vacation if v['adel']])
173         for json_dict in aufrefer.get('annuaire.json'):
174             try:
175                 json_dict['employe'] = employes[json_dict['adel']]
176             except:
177                 json_dict['employe'] = None
178             try:
179                 json_dict['vacation'] = vacation[json_dict['adel']]
180             except:
181                 json_dict['vacation'] = None
182             ldap_dn, ldap_dict = self.json2ldap(json_dict)
183             self.data.append(LdapEntry(ldap_dn, ldap_dict))
184
185     def to_ldif(self):
186         ldif = [ LDAP_INIT_LDIF ]
187         for d in self.data:
188             ldif.append(d.to_ldif(changeType='add'))
189         ldif.append('')
190         return '\n'.join(ldif)
191
192     def search(self, name):
193         return filter(lambda x: x['adel'].find(name) >= 0, self.data)
194
195     def update_ldap(self, base=LDAP_BASE,
196                     host=LDAP_HOST, binddn=LDAP_BINDDN, pwfile=LDAP_PWFILE):
197         pass
198
199 if __name__ == '__main__':
200     annuaire = AnnuaireAuF(LDAP_BASE)
201     annuaire.load()
202     #print "search('andre'): %s" % \
203     #                    map(lambda x: x['adel'], annuaire.search('andre'))
204     print annuaire.to_ldif(),
205