esme.py 13.6 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
11
12
import re
import time
import codecs
Lorenz Steinert's avatar
Lorenz Steinert committed
13
import logging
Lorenz Steinert's avatar
Lorenz Steinert committed
14
import configparser
15
from enum import Flag, auto
16
17
18
19
20
21
22
23
24
import ldap3 as ldap

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


25
class Error(Flag):
26
    """Error constants for the FsrLdap class"""
27
28
29
    UNSEC_SEARCH = auto()
    NO_FILE = auto()
    LDAP_FAIL = auto()
30
    NO_DEFAULT_FILE = auto()
Lorenz Steinert's avatar
Lorenz Steinert committed
31
32
    NO_DIR = auto()
    NO_DEFAULT_DIR = auto()
33

34

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


42
43
44
45
46
47
48
49
50
51
52
53
54
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]


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

Lorenz Steinert's avatar
Lorenz Steinert committed
58
59
    def __init__(self, server, base, share_dir, config, debug=False,
                 timeout=1, testing=None):
60
61
        self.debug = debug

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

        try:
            self.conn.bind()
        except ldap.core.exceptions.LDAPSocketOpenError:
            pass

75
        self.base = base
76
        self.share_dir = share_dir
77
        self.config = config
78

79
    def _get_protokollant_ldap(self):
80
        """search the LDAP for the current user"""
Lorenz Steinert's avatar
Lorenz Steinert committed
81
        logging.info("Fetching Protokollant from LDAP ...")
82
83
        try:
            fil = "(uid="+current_user()+")"
84
85
            if self.conn.search('ou=people,' + self.base, fil,
                                attributes=['displayName']):
Lorenz Steinert's avatar
Lorenz Steinert committed
86
                logging.info("Done")
Lorenz Steinert's avatar
Lorenz Steinert committed
87
                return " ".join(str(self.conn.entries[0]['displayName']).split(", ")[::-1])  # noqa: E501
88
            return Error.UNSEC_SEARCH
89
90
        except ldap.core.exceptions.LDAPException as error_message:
            if self.debug:
Lorenz Steinert's avatar
Lorenz Steinert committed
91
                logging.warning("Couldn't Connect to ldap. \
92
                                Using fallback default. %s" % error_message)
93
            else:
Lorenz Steinert's avatar
Lorenz Steinert committed
94
                logging.warning("Couldn't Connect to ldap. Using fallback default.")  # noqa: E501
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
            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()
Lorenz Steinert's avatar
Lorenz Steinert committed
114
        if isinstance(protokollant, tuple):
115
116
            pass
            protokollant = protokollant[0]
117
118
119
120
121
        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 = ""
122
        return protokollant
123

124
    def _get_fsr_ldap(self):
125
        """get fsr from LDAP"""
Lorenz Steinert's avatar
Lorenz Steinert committed
126
        logging.info("Fetching FSR from LDAP ...")
127
128
        try:
            fil = '(cn=intern)'
Lorenz Steinert's avatar
Lorenz Steinert committed
129
            fsr = []
130
131
132
133
            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,
Lorenz Steinert's avatar
Lorenz Steinert committed
134
135
                                        '(uid='+i+')',
                                        attributes=['displayName']):
136
                        fsr += [str(self.conn.entries[0]['displayName'])]
Lorenz Steinert's avatar
Lorenz Steinert committed
137
138
                fsr.sort()
                fsr = [" ".join(i.split(', ')[::-1]) for i in fsr]
Lorenz Steinert's avatar
Lorenz Steinert committed
139
                logging.info("Done")
Lorenz Steinert's avatar
Lorenz Steinert committed
140
                return fsr
141
            return Error.UNSEC_SEARCH
142
143
        except ldap.core.exceptions.LDAPException as error_message:
            if self.debug:
Lorenz Steinert's avatar
Lorenz Steinert committed
144
145
                logging.warning("Couldn't Connect to ldap. \
                                Using fallback File. %s" % error_message)
146
            else:
Lorenz Steinert's avatar
Lorenz Steinert committed
147
                logging.warning("Couldn't Connect to ldap. Using fallback File.")  # noqa: E501
148
149
150
151
152
153
            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')):
Lorenz Steinert's avatar
Lorenz Steinert committed
154
155
            with codecs.open(os.path.join(share_dir, 'fsr'),
                             'r', 'utf-8') as fobj:
156
157
                fsr = sorted([re.sub(re.escape('\t'), ' ',
                                     line.rstrip('\n'))
158
159
                              for line in fobj.readlines()
                              if line.rstrip('\n')],
160
161
162
163
164
165
166
167
168
169
                             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()
Lorenz Steinert's avatar
Lorenz Steinert committed
170
        if isinstance(fsr, tuple):
171
            fsr = fsr[0]
172
        if isinstance(fsr, Error):
Lorenz Steinert's avatar
Lorenz Steinert committed
173
174
            if fsr & (Error.UNSEC_SEARCH | Error.LDAP_FAIL) \
                    and not gen_fallback:
175
                fsr = self._get_fallback_fsr(share_dir)
176
177
        if isinstance(fsr, Error):
            if fsr & (Error.NO_FILE | Error.UNSEC_SEARCH):
178
                fsr = []
179
        return resort_fsr(fsr, num_cols=self.config['PROTOKOLL'].getint('NUM_COLS'))  # noqa: E501
180

181
    def _get_fsr_extern_ldap(self):
182
        """get the EFSR from LDAP"""
Lorenz Steinert's avatar
Lorenz Steinert committed
183
        logging.info("Fetching EFSR from LDAP ...")
184
185
        try:
            fil = '(cn=extern)'
Lorenz Steinert's avatar
Lorenz Steinert committed
186
            fsr_extern = []
187
188
189
190
            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,
Lorenz Steinert's avatar
Lorenz Steinert committed
191
192
193
                                        '(uid='+i+')',
                                        attributes=['displayName']):
                        fsr_extern += [str(self.conn.entries[0]['displayName'])]  # noqa: E501
Lorenz Steinert's avatar
Lorenz Steinert committed
194
                fsr_extern.sort()
Lorenz Steinert's avatar
Lorenz Steinert committed
195
                fsr_extern = [" ".join(i.split(', ')[::-1]) for i in fsr_extern]  # noqa: E501
Lorenz Steinert's avatar
Lorenz Steinert committed
196
                logging.info("Done")
Lorenz Steinert's avatar
Lorenz Steinert committed
197
                return fsr_extern
198
            return Error.UNSEC_SEARCH
199
200
        except ldap.core.exceptions.LDAPException as error_message:
            if self.debug:
Lorenz Steinert's avatar
Lorenz Steinert committed
201
202
                logging.warning("Couldn't Connect to ldap. \
                                Using fallback File. %s" % error_message)
203
            else:
Lorenz Steinert's avatar
Lorenz Steinert committed
204
                logging.warning("Couldn't Connect to ldap. Using fallback File.")  # noqa: E501
205
206
207
208
209
210
            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')):
Lorenz Steinert's avatar
Lorenz Steinert committed
211
212
            with codecs.open(os.path.join(share_dir, 'fsr_extern'),
                             'r', 'utf-8') as fobj:
213
214
                fsr_extern = sorted([re.sub(re.escape('\t'), ' ',
                                            line.rstrip('\n'))
215
216
                                     for line in fobj.readlines()
                                     if line.rstrip('\n')],
217
218
219
220
221
222
223
224
225
                                    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

226
227
        fsr_extern = self._get_fsr_extern_ldap()
        if isinstance(fsr_extern, tuple):
228
            pass
229
            fsr_extern = fsr_extern[0]
230
        if isinstance(fsr_extern, Error):
Lorenz Steinert's avatar
Lorenz Steinert committed
231
232
            if fsr_extern & (Error.UNSEC_SEARCH | Error.LDAP_FAIL) \
                    and not gen_fallback:
233
                fsr_extern = self._get_fallback_fsr_extern(share_dir)
234
235
        if isinstance(fsr_extern, Error):
            if fsr_extern & (Error.NO_FILE | Error.UNSEC_SEARCH):
236
                fsr_extern = []
237
        return resort_fsr(fsr_extern, num_cols=self.config['PROTOKOLL'].getint('NUM_COLS_EXT'))  # noqa: E501
238

Lorenz Steinert's avatar
Lorenz Steinert committed
239

240
def get_sprecher(config=None):
241
    """get sprecher from user input or config"""
242
    sprecher = ''
243
    if config:
244
        sprecher = config['PROTOKOLL']['Sprecher']
245
    stmp = input("Sprecher ["+sprecher+"]: ")
246
247
248
249
    if stmp != "":
        return stmp
    return sprecher

Lorenz Steinert's avatar
Lorenz Steinert committed
250

251
def get_date(test=False):
Lorenz Steinert's avatar
Lorenz Steinert committed
252
253
254
255
256
257
258
259
260
261
262
263
264
    """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)

265
    dtmp = input("Datum ["+date[0]+"]: ")
266
267
    if dtmp != "":
        day, mon, year = dtmp.split('.')
Lorenz Steinert's avatar
Lorenz Steinert committed
268
269
270
271
        if len(mon) < 2:
            mon = "0" + mon
        if len(day) < 2:
            day = "0" + day
272
273
        date = day + "." + mon + "." + year
        return (date, year, mon, day)
Lorenz Steinert's avatar
Lorenz Steinert committed
274

275
276
    return date

Lorenz Steinert's avatar
Lorenz Steinert committed
277

278
def get_editor(editor):
279
    """get the editor to use from user input"""
280
    etmp = input("Editor ["+editor+"]: ")
281
282
283
    if etmp != "":
        return etmp
    return editor
Lorenz Steinert's avatar
Lorenz Steinert committed
284
285


Lorenz Steinert's avatar
Lorenz Steinert committed
286
287
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
288
289
290
291
292
    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
293
        logging.warning(config_path + ': Not a file')
294
        return Error.NO_FILE
Lorenz Steinert's avatar
Lorenz Steinert committed
295
296
297
298
299
    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
300
        logging.warning('No default File found at '
301
                        + os.path.abspath(os.path.join(rundir,
Lorenz Steinert's avatar
Lorenz Steinert committed
302
                            '../etc/proto.ini.default')))  # noqa: E501
303
        return Error.NO_DEFAULT_FILE
Lorenz Steinert's avatar
Lorenz Steinert committed
304
305
    elif os.path.isfile(os.path.join(rundir,
                                     '../etc/proto.ini')) and not default:
Lorenz Steinert's avatar
Lorenz Steinert committed
306
307
308
        config = configparser.ConfigParser()
        config.read(os.path.join(rundir, '../etc/proto.ini'))
        return config
309
    elif os.path.isfile(os.path.join(rundir, '../etc/proto.ini.default')):
Lorenz Steinert's avatar
Lorenz Steinert committed
310
        logging.warning('No non default ini file found using default file.')
311
312
313
        config = configparser.ConfigParser()
        config.read(os.path.join(rundir, '../etc/proto.ini.default'))
        return config
Lorenz Steinert's avatar
Lorenz Steinert committed
314
    logging.warning('No ini file found. Using hardcoded defaults.')
315
    return Error.NO_DEFAULT_FILE
Lorenz Steinert's avatar
Lorenz Steinert committed
316
317
318


def get_share(rundir, config=None, sharedir=None):
Lorenz Steinert's avatar
Lorenz Steinert committed
319
    """get the location of the share directory"""
Lorenz Steinert's avatar
Lorenz Steinert committed
320
321
322
    if sharedir:
        if os.path.isdir(sharedir):
            return sharedir
Lorenz Steinert's avatar
Lorenz Steinert committed
323
        logging.warning(sharedir + ': Not a Directory')
Lorenz Steinert's avatar
Lorenz Steinert committed
324
        return Error.NO_DIR
Lorenz Steinert's avatar
Lorenz Steinert committed
325
    elif config:
326
327
        if os.path.isdir(config['PROTOKOLL']['share']):
            return config['PROTOKOLL']['share']
Lorenz Steinert's avatar
Lorenz Steinert committed
328
        logging.warning(config['PROTOKOLL']['share'] + ': Not a Directory')
Lorenz Steinert's avatar
Lorenz Steinert committed
329
        return Error.NO_DIR
Lorenz Steinert's avatar
Lorenz Steinert committed
330
331
    elif os.path.isdir(os.path.join(rundir, '../share')):
        return os.path.join(rundir, '../share')
Lorenz Steinert's avatar
Lorenz Steinert committed
332
    logging.warning('No share Directory found at '
333
                    + os.path.abspath(os.path.join(rundir, '../share')))
Lorenz Steinert's avatar
Lorenz Steinert committed
334
    return Error.NO_DEFAULT_DIR
Lorenz Steinert's avatar
Lorenz Steinert committed
335

Lorenz Steinert's avatar
Lorenz Steinert committed
336

Lorenz Steinert's avatar
Lorenz Steinert committed
337
def get_path(rundir, config=None, path=None):
Lorenz Steinert's avatar
Lorenz Steinert committed
338
    """get the location of the protokoll directory"""
Lorenz Steinert's avatar
Lorenz Steinert committed
339
340
341
    if path:
        if os.path.isdir(path):
            return path
Lorenz Steinert's avatar
Lorenz Steinert committed
342
        logging.warning(path + ': Not a Directory')
Lorenz Steinert's avatar
Lorenz Steinert committed
343
        return Error.NO_DIR
Lorenz Steinert's avatar
Lorenz Steinert committed
344
    elif config:
345
346
        if os.path.isdir(config['PROTOKOLL']['path']):
            return config['PROTOKOLL']['path']
Lorenz Steinert's avatar
Lorenz Steinert committed
347
        logging.warning(config['PROTOKOLL']['path'] + ': Not a Directory')
Lorenz Steinert's avatar
Lorenz Steinert committed
348
        return Error.NO_DIR
Lorenz Steinert's avatar
Lorenz Steinert committed
349
350
    elif os.path.isdir(os.path.join(rundir, '../..')):
        return os.path.join(rundir, '../..')
Lorenz Steinert's avatar
Lorenz Steinert committed
351
352
    logging.warning('No Protokoll Directory found at '
                    + os.path.abspath(os.path.join(rundir, '../..')))
Lorenz Steinert's avatar
Lorenz Steinert committed
353
    return Error.NO_DEFAULT_DIR
354

355

356
357
358
359
def get_ldap_server(config=None, server=None):
    """get the ldap server for the fsr lookup"""
    if server:
        return server
360
    if config:
361
        return config['SERVER']['NAME']
Lorenz Steinert's avatar
Lorenz Steinert committed
362
    logging.warning('No LDAP Server supplied. Triing to use "rincwind.fs.physik.uni-kl.de".')  # noqa: E501
363
    return "rincewind.fs.physik.uni-kl.de"
364

365

366
367
368
369
def get_ldap_base(config=None, base=None):
    """get the search base for the ldap server"""
    if base:
        return base
370
    if config:
371
        return config['SERVER']['BASE']
Lorenz Steinert's avatar
Lorenz Steinert committed
372
    logging.warning('No LDAP search base supplied. '
373
                    'Triing to use "dc=fs,dc=physik,dc=uni-kl,dc=de".')
374
    return "dc=fs,dc=physik,dc=uni-kl,dc=de"
375

376

377
378
379
380
def get_server_timeout(config=None, test=None):
    """get the timeout for the connection to the ldap server"""
    if test:
        return 1
381
    if config:
382
        return config['SERVER'].getint('connect_timeout')
383
    return 10