esme.py 12.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 24 15:35:04 2017

@author: Lorenz Steinert
"""

import os
Lorenz Steinert's avatar
Lorenz Steinert committed
10
import re
Lorenz Steinert's avatar
Lorenz Steinert committed
11
import sys
Lorenz Steinert's avatar
Lorenz Steinert committed
12
13
import time
import codecs
Lorenz Steinert's avatar
Lorenz Steinert committed
14
import logging
Lorenz Steinert's avatar
Lorenz Steinert committed
15
import configparser
16
from enum import Flag, auto
17
18
19
20
21
22
23
24
25
import ldap3 as ldap

try:
    import pwd
except ImportError:
    import getpass
    pwd = None


26
class Error(Flag):
27
    """Error constants for the FsrLdap class"""
28
29
30
    UNSEC_SEARCH = auto()
    NO_FILE = auto()
    LDAP_FAIL = auto()
31
32
    NO_DEFAULT_FILE = auto()

33

34
35
36
37
38
39
40
def current_user():
    """get the current user"""
    if pwd:
        return pwd.getpwuid(os.geteuid()).pw_name
    return getpass.getuser()


41
42
43
44
45
46
47
48
49
50
51
52
53
def resort_fsr(fsr, num_cols=5):
    """resort list for pascal style sorting"""
    fsr += [""] * (num_cols - len(fsr) % num_cols)
    depth = len(fsr) // num_cols

    tmp = [[] for i in range(depth)]

    for index, elem in zip(range(len(fsr)), fsr):
        tmp[index % depth] += [elem]

    return [j for i in tmp for j in i]


54
class FsrLdap:
55
56
    """Class die die protokoll .tex datei erstellt"""

57

58
    def __init__(self, server, base, share_dir, config, debug=False, timeout=1, testing=None):
59
60
        self.debug = debug

61
        self.server = ldap.Server(server,
Lorenz Steinert's avatar
Lorenz Steinert committed
62
                                  connect_timeout=timeout)
63
64
65
66
67
        if not testing:
            self.conn = ldap.Connection(self.server)
        else:
            self.conn = ldap.Connection(self.server, client_strategy=ldap.MOCK_SYNC)
        self.conn.bind()
68
        self.base = base
69
        self.share_dir = share_dir
70
        self.config = config
71

72
    def _get_protokollant_ldap(self):
73
        """search the LDAP for the current user"""
Lorenz Steinert's avatar
Lorenz Steinert committed
74
        logging.info("Fetching Protokollant from LDAP ...")
75
76
        try:
            fil = "(uid="+current_user()+")"
77
78
            if self.conn.search('ou=people,' + self.base, fil,
                                attributes=['displayName']):
Lorenz Steinert's avatar
Lorenz Steinert committed
79
                logging.info("Done")
80
                return " ".join(str(self.conn.entries[0]['displayName']).split(", ")[::-1])
81
            return Error.UNSEC_SEARCH
82
83
        except ldap.core.exceptions.LDAPException as error_message:
            if self.debug:
Lorenz Steinert's avatar
Lorenz Steinert committed
84
                logging.warning("Couldn't Connect to ldap. \
85
                                Using fallback default. %s" % error_message)
86
            else:
Lorenz Steinert's avatar
Lorenz Steinert committed
87
                logging.warning("Couldn't Connect to ldap. Using fallback default.")
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
            return Error.LDAP_FAIL

    @staticmethod
    def _get_fallback_protokollant(config=None):
        """get protokollant from user input or config"""
        protokollant = ''
        if config:
            protokollant = config['PROTOKOLL']['Protokollant']
        ptmp = input("Protokollant ["+protokollant+"]: ")
        if ptmp != "":
            return ptmp
        return protokollant

    def get_protokollant(self, config=None):
        """get protokollant"""
        if not config:
            config = self.config

        protokollant = self._get_protokollant_ldap()
        if  isinstance(protokollant, tuple):
            pass
            protokollant = protokollant[0]
110
111
112
113
114
        elif isinstance(protokollant, Flag):
            if protokollant in Error.UNSEC_SEARCH | Error.LDAP_FAIL:
                protokollant = self._get_fallback_protokollant(config)
            if protokollant == Error.NO_FILE:
                protokollant = ""
115
        return protokollant
116

117
    def _get_fsr_ldap(self):
118
        """get fsr from LDAP"""
Lorenz Steinert's avatar
Lorenz Steinert committed
119
        logging.info("Fetching FSR from LDAP ...")
120
121
        try:
            fil = '(cn=intern)'
Lorenz Steinert's avatar
Lorenz Steinert committed
122
            fsr = []
123
124
125
126
127
128
            if self.conn.search('ou=group,'+self.base, fil,
                                attributes=['memberUid']):
                for i in self.conn.entries[0]['memberUid']:
                    if self.conn.search('ou=people,' + self.base,
                                        '(uid='+i+')', attributes=['displayName']):
                        fsr += [str(self.conn.entries[0]['displayName'])]
Lorenz Steinert's avatar
Lorenz Steinert committed
129
130
                fsr.sort()
                fsr = [" ".join(i.split(', ')[::-1]) for i in fsr]
Lorenz Steinert's avatar
Lorenz Steinert committed
131
                logging.info("Done")
Lorenz Steinert's avatar
Lorenz Steinert committed
132
                return fsr
133
            return Error.UNSEC_SEARCH
134
135
        except ldap.core.exceptions.LDAPException as error_message:
            if self.debug:
Lorenz Steinert's avatar
Lorenz Steinert committed
136
137
                logging.warning("Couldn't Connect to ldap. \
                                Using fallback File. %s" % error_message)
138
            else:
Lorenz Steinert's avatar
Lorenz Steinert committed
139
                logging.warning("Couldn't Connect to ldap. Using fallback File.")
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
            return (Error.LDAP_FAIL, error_message)

    @staticmethod
    def _get_fallback_fsr(share_dir):
        """get fsr form fallback file"""
        if os.path.isfile(os.path.join(share_dir, 'fsr')):
            with codecs.open(os.path.join(share_dir, 'fsr'), 'r', 'utf-8') as fobj:
                fsr = sorted([re.sub(re.escape('\t'), ' ',
                                     line.rstrip('\n'))
                              for line in fobj.readlines()],
                             key=lambda x: ''.join(x.split(' ')[::-1]))
            return fsr
        return Error.NO_FILE

    def get_fsr(self, share_dir=None, gen_fallback=None):
        """get fsr"""
        if not share_dir:
            share_dir = self.share_dir

        fsr = self._get_fsr_ldap()
        if  isinstance(fsr, tuple):
            pass
            fsr = fsr[0]
163
164
165
166
167
        elif isinstance(fsr, Flag):
            if fsr == Error.UNSEC_SEARCH | Error.LDAP_FAIL and not gen_fallback:
                fsr = self._get_fallback_fsr(share_dir)
            if fsr == Error.NO_FILE | Error.UNSEC_SEARCH:
                fsr = []
168
        return resort_fsr(fsr)
169

170
    def _get_fsr_extern_ldap(self):
171
        """get the EFSR from LDAP"""
Lorenz Steinert's avatar
Lorenz Steinert committed
172
        logging.info("Fetching EFSR from LDAP ...")
173
174
        try:
            fil = '(cn=extern)'
Lorenz Steinert's avatar
Lorenz Steinert committed
175
            fsr_extern = []
176
177
178
179
180
181
            if self.conn.search('ou=group,'+self.base, fil,
                                attributes=['memberUid']):
                for i in self.conn.entries[0]['memberUid']:
                    if self.conn.search('ou=people,' + self.base,
                                        '(uid='+i+')', attributes=['displayName']):
                        fsr_extern += [str(self.conn.entries[0]['displayName'])]
Lorenz Steinert's avatar
Lorenz Steinert committed
182
183
                fsr_extern.sort()
                fsr_extern = [" ".join(i.split(', ')[::-1]) for i in fsr_extern]
Lorenz Steinert's avatar
Lorenz Steinert committed
184
                logging.info("Done")
Lorenz Steinert's avatar
Lorenz Steinert committed
185
                return fsr_extern
186
            return Error.UNSEC_SEARCH
187
188
        except ldap.core.exceptions.LDAPException as error_message:
            if self.debug:
Lorenz Steinert's avatar
Lorenz Steinert committed
189
190
                logging.warning("Couldn't Connect to ldap. \
                                Using fallback File. %s" % error_message)
191
            else:
Lorenz Steinert's avatar
Lorenz Steinert committed
192
                logging.warning("Couldn't Connect to ldap. Using fallback File.")
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
            return (Error.LDAP_FAIL, error_message)

    @staticmethod
    def _get_fallback_fsr_extern(share_dir):
        """get fsr_extern from fallback file"""
        if os.path.isfile(os.path.join(share_dir, 'fsr_extern')):
            with codecs.open(os.path.join(share_dir, 'fsr_extern'), 'r', 'utf-8') as fobj:
                fsr_extern = sorted([re.sub(re.escape('\t'), ' ',
                                            line.rstrip('\n'))
                                     for line in fobj.readlines()],
                                    key=lambda x: ''.join(x.split(' ')[::-1]))
            return fsr_extern
        return Error.NO_FILE

    def get_fsr_extern(self, share_dir=None, gen_fallback=None):
        """get fsr extern"""
        if not share_dir:
            share_dir = self.share_dir

212
213
        fsr_extern = self._get_fsr_extern_ldap()
        if isinstance(fsr_extern, tuple):
214
            pass
215
216
217
218
219
220
221
            fsr_extern = fsr_extern[0]
        if isinstance(fsr_extern, Flag):
            if fsr_extern == Error.UNSEC_SEARCH | Error.LDAP_FAIL and not gen_fallback:
                fsr_extern = self._get_fallback_fsr_extern(share_dir)
            if fsr_extern == Error.NO_FILE | Error.UNSEC_SEARCH:
                fsr_extern = []
        return resort_fsr(fsr_extern)
222

Lorenz Steinert's avatar
Lorenz Steinert committed
223

224
def get_sprecher(config=None):
225
    """get sprecher from user input or config"""
226
    sprecher = ''
227
    if config:
228
        sprecher = config['PROTOKOLL']['Sprecher']
229
    stmp = input("Sprecher ["+sprecher+"]: ")
230
231
232
233
    if stmp != "":
        return stmp
    return sprecher

Lorenz Steinert's avatar
Lorenz Steinert committed
234

235
def get_date(test=False):
Lorenz Steinert's avatar
Lorenz Steinert committed
236
237
238
239
240
241
242
243
244
245
246
247
248
    """get current date then ask user"""
    if test:
        date = ('01.01.2000', '2000', '01', '01')
    else:
        year = str(time.localtime()[0])
        mon = str(time.localtime()[1])
        if len(mon) < 2:
            mon = "0"+mon
        day = str(time.localtime()[2])
        if len(day) < 2:
            day = "0"+day
        date = (day + '.' + mon + '.' + year, year, mon, day)

249
    dtmp = input("Datum ["+date[0]+"]: ")
250
251
    if dtmp != "":
        day, mon, year = dtmp.split('.')
Lorenz Steinert's avatar
Lorenz Steinert committed
252
253
254
255
        if len(mon) < 2:
            mon = "0" + mon
        if len(day) < 2:
            day = "0" + day
256
257
        date = day + "." + mon + "." + year
        return (date, year, mon, day)
Lorenz Steinert's avatar
Lorenz Steinert committed
258

259
260
    return date

Lorenz Steinert's avatar
Lorenz Steinert committed
261

262
def get_editor(editor):
263
    """get the editor to use from user input"""
264
    etmp = input("Editor ["+editor+"]: ")
265
266
267
    if etmp != "":
        return etmp
    return editor
Lorenz Steinert's avatar
Lorenz Steinert committed
268
269


Lorenz Steinert's avatar
Lorenz Steinert committed
270
271
def get_config(rundir, config_path=None, default=False):
    """get the location of the config file and load configparser"""
Lorenz Steinert's avatar
Lorenz Steinert committed
272
273
274
275
276
    if config_path:
        if os.path.isfile(config_path):
            config = configparser.ConfigParser()
            config.read(config_path)
            return config
Lorenz Steinert's avatar
Lorenz Steinert committed
277
        logging.warning(config_path + ': Not a file')
278
        return Error.NO_FILE
Lorenz Steinert's avatar
Lorenz Steinert committed
279
280
281
282
283
    elif default:
        if os.path.isfile(os.path.join(rundir, '../etc/proto.ini.default')):
            config = configparser.ConfigParser()
            config.read(os.path.join(rundir, '../etc/proto.ini.default'))
            return config
Lorenz Steinert's avatar
Lorenz Steinert committed
284
        logging.warning('No default File found at '
285
286
                        + os.path.abspath(os.path.join(rundir,
                                                       '../etc/proto.ini.default')))
287
        return Error.NO_DEFAULT_FILE
Lorenz Steinert's avatar
Lorenz Steinert committed
288
289
    elif os.path.isfile(os.path.join(rundir,
                                     '../etc/proto.ini')) and not default:
Lorenz Steinert's avatar
Lorenz Steinert committed
290
291
292
        config = configparser.ConfigParser()
        config.read(os.path.join(rundir, '../etc/proto.ini'))
        return config
293
    elif os.path.isfile(os.path.join(rundir, '../etc/proto.ini.default')):
Lorenz Steinert's avatar
Lorenz Steinert committed
294
        logging.warning('No non default ini file found using default file.')
295
296
297
        config = configparser.ConfigParser()
        config.read(os.path.join(rundir, '../etc/proto.ini.default'))
        return config
Lorenz Steinert's avatar
Lorenz Steinert committed
298
    logging.warning('No ini file found. Using hardcoded defaults.')
299
    return Error.NO_DEFAULT_FILE
Lorenz Steinert's avatar
Lorenz Steinert committed
300
301
302


def get_share(rundir, config=None, sharedir=None):
Lorenz Steinert's avatar
Lorenz Steinert committed
303
    """get the location of the share directory"""
Lorenz Steinert's avatar
Lorenz Steinert committed
304
305
306
    if sharedir:
        if os.path.isdir(sharedir):
            return sharedir
Lorenz Steinert's avatar
Lorenz Steinert committed
307
        logging.warning(sharedir + ': Not a Directory')
308
        sys.exit(2)
Lorenz Steinert's avatar
Lorenz Steinert committed
309
    elif config:
310
311
        if os.path.isdir(config['PROTOKOLL']['share']):
            return config['PROTOKOLL']['share']
Lorenz Steinert's avatar
Lorenz Steinert committed
312
        logging.warning(config['PROTOKOLL']['share'] + ': Not a Directory')
313
        sys.exit(2)
Lorenz Steinert's avatar
Lorenz Steinert committed
314
315
    elif os.path.isdir(os.path.join(rundir, '../share')):
        return os.path.join(rundir, '../share')
Lorenz Steinert's avatar
Lorenz Steinert committed
316
    logging.warning('No share Directory found at '
317
                    + os.path.abspath(os.path.join(rundir, '../share')))
318
    sys.exit(1)
Lorenz Steinert's avatar
Lorenz Steinert committed
319
320
321


def get_path(rundir, config=None, path=None):
Lorenz Steinert's avatar
Lorenz Steinert committed
322
    """get the location of the protokoll directory"""
Lorenz Steinert's avatar
Lorenz Steinert committed
323
324
325
    if path:
        if os.path.isdir(path):
            return path
Lorenz Steinert's avatar
Lorenz Steinert committed
326
        logging.warning(path + ': Not a Directory')
327
        sys.exit(2)
Lorenz Steinert's avatar
Lorenz Steinert committed
328
    elif config:
329
330
        if os.path.isdir(config['PROTOKOLL']['path']):
            return config['PROTOKOLL']['path']
Lorenz Steinert's avatar
Lorenz Steinert committed
331
        logging.warning(config['PROTOKOLL']['path'] + ': Not a Directory')
332
        sys.exit(2)
Lorenz Steinert's avatar
Lorenz Steinert committed
333
334
    elif os.path.isdir(os.path.join(rundir, '../..')):
        return os.path.join(rundir, '../..')
Lorenz Steinert's avatar
Lorenz Steinert committed
335
336
    logging.warning('No Protokoll Directory found at '
                    + os.path.abspath(os.path.join(rundir, '../..')))
337
    sys.exit(1)
338

339

340
341
342
343
def get_ldap_server(config=None, server=None):
    """get the ldap server for the fsr lookup"""
    if server:
        return server
344
    if config:
345
        return config['SERVER']['NAME']
Lorenz Steinert's avatar
Lorenz Steinert committed
346
    logging.warning('No LDAP Server supplied. Triing to use "rincwind.fs.physik.uni-kl.de".')
347
    return "rincewind.fs.physik.uni-kl.de"
348

349

350
351
352
353
def get_ldap_base(config=None, base=None):
    """get the search base for the ldap server"""
    if base:
        return base
354
    if config:
355
        return config['SERVER']['BASE']
356
357
    logging.warning('No LDAP search base supplied. ' \
                    'Triing to use "dc=fs,dc=physik,dc=uni-kl,dc=de".')
358
    return "dc=fs,dc=physik,dc=uni-kl,dc=de"
359

360

361
362
363
364
def get_server_timeout(config=None, test=None):
    """get the timeout for the connection to the ldap server"""
    if test:
        return 1
365
    if config:
366
        return config['SERVER'].getint('connect_timeout')
367
    return 10