Projet

Général

Profil

cadoles.py

Emmanuel GARETTE, 05/01/2012 09:31

Télécharger (14,8 ko)

 
1
# -*- coding: utf-8 -*-
2
###########################################################################
3
# EOLE - 2011
4
# Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
5
# Licence CeCill  cf /root/LicenceEole.txt
6
# eole@ac-dijon.fr
7
#
8
# modifié par Cadoles (http://www.cadoles.com/)
9
# contact@cadoles.com
10
#
11
###########################################################################
12

    
13
import time
14
import locale
15
from shutil import rmtree
16
from os import system, makedirs, symlink, remove, chown, chmod, listdir, fchmod
17
from os.path import join, islink, isfile, basename, isdir, getmtime
18
from datetime import datetime
19
import ldap
20

    
21
from scribe.cadolesldapconf import ROOT_DN, ldap_server, ldap_passwd, SUFFIX, \
22
        HOME_PATH, SMB_SERVEUR, USER_FILTER, GROUP_FILTER, SHARE_FILTER
23

    
24
PATH_SCRIPTS = '/home/netlogon/scripts'
25
SCRIPTS_EXT = '.txt'
26

    
27
def logon(user, ostype, machine, adresse_ip):
28

    
29
    def ldap_search(ldap_filter, attrib=None, one=False):
30
        result = ldap_conn.search_s(SUFFIX, ldap.SCOPE_SUBTREE,
31
                                    ldap_filter, attrib)
32
        if len(result) > 0 and len(result[0]) == 2:
33
            if one:
34
                return result[0][1]
35
            else:
36
                return result
37
        else:
38
            return {}
39

    
40
    ldap_conn = ldap.open(ldap_server)
41
    ldap_conn.simple_bind_s(ROOT_DN, ldap_passwd)
42
    user_attrib = ldap_search("(&%s(uid=%s))" % (USER_FILTER, user),
43
                ['objectClass', 'homeDirectory', 'sambaHomeDrive', 'uidNumber',
44
                'gidNumber'], True)
45
    userclass = get_userclass_name(user_attrib.get('objectClass', None))
46
    if not userclass:
47
        raise Exception("utilisateur %s inconnu" % user)
48

    
49
    if userclass not in ['administratif', 'eleve', 'enseignant']:
50
        raise Exception("l'utilisateur %s n'est pas autorisé à se connecter" % user)
51

    
52
    # si le fichier existe déjà et qu'il a moins de 1min on loggue juste la
53
    # connexion
54
    netlogon = '/home/netlogon/%s%s.txt' % (user, ostype)
55
    if isfile(netlogon):
56
        diff = time.time() - getmtime(netlogon)
57
    else:
58
        diff = 600
59
    prim_group = None
60
    gidnumber = user_attrib['gidNumber'][0].strip()
61
    if diff > 60:
62
        """ construction des scripts de connexion windows
63
        le fichier est du format .bat pour Win95 et .txt pour le reste
64
        """
65
        homedir = user_attrib['homeDirectory'][0].strip()
66
        homedrive = user_attrib['sambaHomeDrive'][0].strip()
67
        uid = int(user_attrib['uidNumber'][0].strip())
68

    
69
        groups = []
70
        group_filter = ''
71
        for dn, grp in ldap_search("(&%s(memberUid=%s))" % (GROUP_FILTER, user),
72
                attrib=['cn', 'gidNumber']):
73
            name = grp['cn'][0]
74
            if grp['gidNumber'][0] == gidnumber:
75
                prim_group = name
76
            groups.append(name)
77
            group_filter += '(sambaShareGroup=%s)' % name
78

    
79
        shares = []
80
        for share in ldap_search('(&%s(|%s))' % (SHARE_FILTER, group_filter),
81
                    ['sambaShareName', 'sambaFilePath', 'sambaShareURI',
82
                    'sambaShareDrive']):
83
            if share[1].has_key('sambaShareDrive'):
84
                drive = share[1]['sambaShareDrive'][0]
85
            else:
86
                drive = ''
87
            shares.append({'name': share[1]['sambaShareName'][0],
88
                            'path': share[1]['sambaFilePath'][0],
89
                            'uri': share[1]['sambaShareURI'][0],
90
                            'drive': drive})
91

    
92
        # génération du fichier client
93
        gen_fich(user, groups, shares, machine, ostype, homedrive, userclass,
94
                netlogon)
95
        # mise à jour de /home/u/user/.ftp/
96
        gen_ftpdir(uid, homedir, shares)
97
        # mise à jour de /home/u/user/groupes/
98
        gen_groupedir(uid, homedir, shares)
99
        # répertoire devoirs
100
        gen_devdir(user, uid, homedir, userclass)
101

    
102
    if not prim_group:
103
        prim_group = ldap_search("(&%s(gidNumber=%s))" % (GROUP_FILTER,
104
                gidnumber), ['displayName'], True)['displayName'][0]
105
    ldap_conn.unbind()
106

    
107
    # enregistrement de la connexion
108
    # après puisque si l'utilisateur n'est pas censé se connecter
109
    # le script n'arrive pas jusqu'à là
110
    log_connexion(user, prim_group, machine, ostype, adresse_ip)
111

    
112
def get_userclass_name(objectClass):
113
    """
114
        Renvoie le nom du module gérant les objets de classe : objectClass
115
    """
116
    if objectClass == None:
117
        return None
118
    users_objectClass = {
119
            'Eleves': 'eleve',
120
            'administrateur': 'enseignant',
121
            'responsable': 'responsable',
122
            'administratif': 'administratif',
123
            'autre': 'autre',
124
            }
125
    for objectclass, module in users_objectClass.items():
126
        if objectclass in objectClass:
127
            return module
128
    return None
129

    
130
def gen_devdir(login, uid, homedir, userclass):
131
    """
132
        Partie commune pour _gen_devoirdir
133
    """
134
    if userclass not in ['eleve', 'enseignant']:
135
        return
136
    perso = join(homedir, 'perso')
137
    # le partage "devoirs" /home/l/login/devoirs
138
    dev_part = join(homedir, 'devoirs')
139
    # le dossier U:\devoirs /home/l/login/perso/devoirs
140
    dev_perso = join(perso, 'devoirs')
141
    cmd = ""
142
    for rep in [dev_part, dev_perso]:
143
        if islink(rep):
144
            remove(rep)
145
        if not isdir(rep):
146
            makedirs(rep, 0750)
147
        else:
148
            cmd += '/bin/chmod -R 0750 %s;' % rep
149
        cmd += '/bin/chown -PR %s %s;' % (login, rep)
150
        cmd += '/usr/bin/setfacl -PRm u:%s:rwx %s;'%(uid, rep)
151
        cmd += '/usr/bin/setfacl -dPRm u:%s:rwx %s;'%(uid, rep)
152

    
153
    if userclass == 'enseignant':
154
        # dossier contenant les devoirs et les données à distribuer du prof
155
        # U:\devoirs\.distribues ou \\scribe\devoirs\.distribues
156
        dev_dist_dir = join(HOME_PATH, 'workgroups/devoirs', login)
157
        if not isdir(dev_dist_dir):
158
            makedirs(dev_dist_dir, 0755)
159
        else:
160
            cmd += '/bin/chmod -R 0755 %s;' % dev_dist_dir
161
        cmd += '/usr/bin/setfacl -PRbk %s;' % dev_dist_dir
162
        cmd += '/usr/bin/setfacl -PRm u:%s:rwx %s;' % (uid, dev_dist_dir)
163
        cmd += '/usr/bin/setfacl -dPRm u:%s:rwx %s;' % (uid, dev_dist_dir)
164
        for rep in [dev_part, dev_perso]:
165
            link = join(rep, '.distribues') # le cacher avec un "."
166
            if not islink(link):
167
                symlink(dev_dist_dir, link)
168
    system(cmd)
169

    
170
def gen_ftpdir(uid, homedir, shares):
171
    """
172
        Gestion du répertoire ".ftp"
173
    """
174
    ftpdir = join(homedir, '.ftp')
175
    if isdir(ftpdir):
176
        rmtree(ftpdir)
177
    makedirs(ftpdir, 0500)
178
    chown(ftpdir, uid, -1)
179
    #les ACLs ne sont pas mises
180
    #system('setfacl -bk %s' % ftpdir)
181
    #chmod(ftpdir, 0500)
182
    homedir = join(HOME_PATH, homedir[6:])
183
    symlink(join(homedir, 'perso'), join(ftpdir, 'perso'))
184
    for share in shares:
185
        if share['name'] not in ['icones$', 'groupes']:
186
            if HOME_PATH != '/home':
187
                share['path'] = share['path'].replace('/home',
188
                        HOME_PATH)
189
            symlink(share['path'], join(ftpdir, share['name']))
190

    
191
def gen_groupedir(uid, homedir, shares):
192
    """
193
        Gestion du répertoire "groupes"
194
    """
195
    groupedir = join(homedir, 'groupes')
196
    if isdir(groupedir):
197
        rmtree(groupedir)
198
    makedirs(groupedir, 0500)
199
    chown(groupedir, uid, -1)
200
    #les ACLs ne sont pas mise
201
    #system('setfacl -bk %s' % groupedir)
202
    for share in shares:
203
        # sinon partage avec lettre de lecteur
204
        if share['drive'] == "":
205
            # lien dans le répertoire "groupes"
206
            symlink(share['path'], join(groupedir, share['name']))
207

    
208
def log_connexion(user, primgrp, machine, ostype, adresse_ip):
209
    """
210
    Enregistrement de la connexion
211
    """
212
    locale.setlocale(locale.LC_ALL, '')
213
    try:
214
        sdate = datetime.now().strftime("%a %d %b %Y %H:%M").capitalize()
215
        cmd = 'echo CONNECTION %s %s %s %s %s %s' % (sdate, user,
216
                primgrp, machine, ostype, adresse_ip)
217
        system('%s >> /var/log/samba/%s.log' % (cmd, machine))
218
        system('%s >> /var/log/samba/connexions.log' % (cmd))
219
    except:
220
        pass
221

    
222
#############################################################
223
## Génération des fichiers de logon
224
#############################################################
225

    
226
def gen_fich(user, groups, shares, machine, ostype, homedrive, userclass,
227
        netlogon):
228
    """
229
    Génération du fichier lu par le client
230
    """
231
    script = ""
232
    # gestion des groupes et des partages
233
    for share in shares:
234
        # sinon créé un lien symbolique
235
        if share['drive'] != "":
236
            # partage avec lettre de lecteur
237
            if ostype == "Win95":
238
                script += gen_lecteur_bloc_win95(share['drive'],
239
                        share['uri'])
240
            else:
241
                script += gen_lecteur_bloc(share['drive'], share['uri'])
242
    # montage du répertoire personnel pour 9x et Samba
243
    #FIXME: pourquoi Samba ?
244
    if ostype in ['Win95', 'Samba']:
245
        script += gen_lecteur_bloc_win95(homedrive, '\\\\%s\\PERSO' %
246
                SMB_SERVEUR)
247

    
248
    if ostype == 'Win95':
249
        """Gestion "à l'ancienne" pour Win9x/Me
250
        exécuté par le service et logon.py(.exe) sur les clients
251
        """
252
        script += gen_vnc_line(userclass)
253
        script += gen_esu()
254

    
255
    # gestion des scripts additionnels
256
    debut, fin = get_scripts(user, machine, ostype, groups)
257
    # écriture du fichier
258
    write_fich(netlogon, debut + script + fin)
259

    
260
def gen_letter_share(lettre, share):
261
    """lettre : supprime les ":", juste la lettre
262
    share : le partage sans \ à la fin
263
    """
264
    while share[-1] == '\\':
265
        share = share[:-1]
266
    return lettre[:1], share
267

    
268
def gen_lecteur_bloc(lettre, share):
269
    """Génère un bloc de ligne pour le montage
270
    d'un lecteur réseau "share" sur la lettre "lettre"
271
    ou sur * sinon (sauf pour win95)
272
    """
273
    lettre, share = gen_letter_share(lettre, share)
274
    return 'lecteur,%s:,%s\r\n' % (lettre, share)
275

    
276
def gen_cmd_line(line, hide=False, nowait=False):
277
    """génère une ligne DOS (avec le bon retour chariot) (pas win95)
278
    """
279
    chaine = 'cmd,%s' % line
280
    if hide:
281
        chaine += ',HIDDEN'
282
    if nowait:
283
        chaine += ',NOWAIT'
284
    return chaine + '\r\n'
285

    
286
def get_scripts(user, machine, ostype, user_groups):
287
    """Gestion des scripts externes
288
    """
289
    # motif à rechercher dans les fichiers externes
290
    # (debut_script %motif% fin_script)
291
    motif_include = [ "%%NetUse%%", "%NetUse%" ]
292
    # chemin par défaut des scripts externes
293
    buffer_debut,  buffer_fin = '', ''
294
    # on traite ceux qui sont présents
295
    for chemin in get_scripts_list(user, machine, ostype, user_groups):
296
        if isfile(chemin):
297
            debut = 1
298
            for line in file(chemin,"r"):
299
                line = line.strip()
300
                if line in motif_include:
301
                    debut = 0
302
                else:
303
                    if debut == 1:
304
                        buffer_debut += parse_line(ostype, line)
305
                    else:
306
                        buffer_fin += parse_line(ostype, line)
307
    return (buffer_debut, buffer_fin)
308

    
309
def get_scripts_list(user, machine, ostype, user_groups):
310
    """On créé la liste des fichiers possibles
311
    +--PATH_SCRIPTS/users/<user>.txt
312
    +--PATH_SCRIPTS/groups/<group>.txt
313
    +--PATH_SCRIPTS/machines/<machine>.txt
314
    +--PATH_SCRIPTS/os/<os>.txt
315
    +--PATH_SCRIPTS/os/<os>/<group>.txt
316
    +--PATH_SCRIPTS/os/<os>/<user>.txt
317
    """
318
    chemins = [join(PATH_SCRIPTS, 'users', '%s%s' % (user, SCRIPTS_EXT))]
319
    chemins.append(join(PATH_SCRIPTS, 'machines', '%s%s' %
320
                (machine, SCRIPTS_EXT)))
321
    for group in user_groups:
322
        chemins.append(join(PATH_SCRIPTS, 'groups', '%s%s' %
323
                    (group, SCRIPTS_EXT)))
324
        chemins.append(join(PATH_SCRIPTS, 'os', '%s' % ostype, '%s%s' %
325
                    (group, SCRIPTS_EXT)))
326
    chemins.append(join(PATH_SCRIPTS, 'os', '%s%s' % (ostype, SCRIPTS_EXT)))
327
    chemins.append(join(PATH_SCRIPTS, 'os', ostype, '%s%s' %
328
                (user, SCRIPTS_EXT)))
329
    return chemins
330

    
331
def parse_line(ostype, line):
332
    items = [ i.strip() for i in line.split(',') ]
333
    if items[0] == 'cmd':
334
        # exécuter une commande
335
        if ostype == "Win95":
336
            return gen_cmd_line_win95(items[1])
337
        else:
338
            hide = 'HIDDEN' in items
339
            nowait = 'NOWAIT' in items
340
            return gen_cmd_line(items[1], hide, nowait)
341

    
342
    elif items[0] == 'lecteur':
343
        # monter un partage
344
        lettre, partage = items[1], items[2]
345
        if ostype == "Win95":
346
            return gen_lecteur_bloc_win95(lettre, partage)
347
        else:
348
            return gen_lecteur_bloc(lettre, partage)
349
    return ''
350

    
351
def write_fich(netlogon, lines):
352
    """Ecrit le fichier
353
    """
354
    fic = file(netlogon, 'w')
355
    fchmod(fic.fileno(), 0644)
356
    #convert to DOS format
357
    #lines = lines.replace('\n', '\r\n')
358
    lines = lines.decode('utf8').encode('cp437')
359
    fic.write(lines)
360
    fic.close()
361

    
362
#########
363
# Win9x #
364
#########
365
def test_esu():
366
    """
367
    vérifie si Esu est utilisé
368
    """
369
    for fic in listdir('/home/esu/Base'):
370
        if fic.lower() == 'esuclnt.exe':
371
            return True
372
    return False
373

    
374
def gen_esu():
375
    """Ajoute la ligne pour Esu pour win95
376
    """
377
    if test_esu():
378
        esuline = '\\\\%s\\ESU\\Base\\esuclnt.exe' % SMB_SERVEUR
379
        return gen_cmd_line_win95(esuline)
380
    return ''
381

    
382
def gen_vnc_line(userclass):
383
    """VNC utilise maintenant login/pass du domaine
384
    ce .reg spécifie le type de controle possible$
385
    (desactive, simple, controle) lors de l'observation pour les Win95
386
    """
387
    vnccontrole = 'regedit /s \\\\%s\\netlogon\\scripts\\os\\Win95\\vnc_controle.reg' % SMB_SERVEUR
388
    vnckill = '%windir%\\eole\\ultravnc\\winvnc -kill'
389
    vncservice = '%windir%\\eole\\ultravnc\\winvnc -service'
390
    bloc = gen_cmd_line_win95(vnccontrole)
391
    bloc += gen_cmd_line_win95(vnckill)
392
    if userclass == 'eleve':
393
        bloc += gen_cmd_line_win95(vncservice)
394
    return bloc
395

    
396
def gen_cmd_line_win95(line):
397
    """génère une ligne DOS Win95 (avec le bon retour chariot)
398
    """
399
    saut_ligne = chr(13)+chr(10)
400
    return '%s%s' % (line, saut_ligne)
401

    
402
def gen_lecteur_bloc_win95(lettre, share):
403
    """Génère un bloc de ligne pour le montage
404
    d'un lecteur réseau "share" sur la lettre "lettre"
405
    ou sur * sinon pour win95
406
    """
407
    lettre, share = gen_letter_share(lettre, share)
408
    sharename = share.split('\\')[-1].replace('$', '')
409
    # on définit la commande net use selon le type d'os (NT ou 9X)
410
    label = '%s%s' % (lettre, sharename)
411
    chaine_net_use = 'NET USE '
412
    bloc = gen_cmd_line_win95('IF EXIST %s:\\nul goto err%s' % (lettre, label))
413
    bloc += gen_cmd_line_win95('%s %s: %s'%(chaine_net_use, lettre, share))
414
    bloc += gen_cmd_line_win95('goto suit%s'%(label))
415
    bloc += gen_cmd_line_win95(':err%s'%(label))
416
    bloc += gen_cmd_line_win95('%s * %s'%(chaine_net_use, share))
417
    bloc += gen_cmd_line_win95(':suit%s'%(label))
418
    return bloc
419