py.lib
13:cab7fedf8432 Browse Files
.. Реструктуризация
Alarm.py awNet/__init__.py awNet/db/__init__.py awNet/db/pg.py awNet/db/sqlite.py awNet/webapp/__init__.py awNet/webapp/tool/__init__.py awNet/webapp/tool/url.py awNet/webapp/win.py db/__init__.py db/ldap.py db/pg/psy.py db/pg/py.py db/sqlite.py ldapUtil.py ldap_utils/ldap_passwd_changer.py ldap_utils/ldap_util.py log/slog_syslogger.py log/slog_syslogger_tiny.py log/syslogger.py webapp/__init__.py webapp/tool/__init__.py webapp/tool/url.py webapp/win.py
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/Alarm.py Fri Jul 24 14:40:37 2020 +0300 1.3 @@ -0,0 +1,25 @@ 1.4 +#!/usr/bin/python 1.5 +# -*- coding: utf-8 -*- 1.6 + 1.7 +from signal import SIGALRM, alarm, signal 1.8 + 1.9 +class AlarmTimeout(Exception): pass 1.10 + 1.11 +def _handler(sig, frame): 1.12 + raise AlarmTimeout('Operation timeout') 1.13 + 1.14 +class MkAlarm(object): 1.15 + def __init__(self, timeout): 1.16 + signal(SIGALRM, _handler) 1.17 + alarm(timeout) 1.18 + 1.19 + def __enter__(self): 1.20 + pass 1.21 + 1.22 + def __exit__(self, eType, eObj, tb): 1.23 + if eObj == None: 1.24 + alarm(0) 1.25 + 1.26 + def __del__(self): 1.27 + alarm(0) 1.28 + 1.29 \ No newline at end of file
2.1 --- a/awNet/__init__.py Tue Nov 12 21:28:23 2019 +0300 2.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 2.3 @@ -1,4 +0,0 @@ 2.4 -# -*- coding: utf-8 -*- 2.5 -# --- 2.6 -# Контейнер модулей собственного производства общего назначения 2.7 -# --- 2.8 \ No newline at end of file
3.1 --- a/awNet/db/__init__.py Tue Nov 12 21:28:23 2019 +0300 3.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 3.3 @@ -1,4 +0,0 @@ 3.4 -# -*- coding: utf-8 -*- 3.5 -# --- 3.6 -# Модули для работы с базами данных 3.7 -# --- 3.8 \ No newline at end of file
4.1 --- a/awNet/db/pg.py Tue Nov 12 21:28:23 2019 +0300 4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 4.3 @@ -1,31 +0,0 @@ 4.4 -import postgresql.driver as DBM 4.5 -import postgresql.exceptions as DBErr 4.6 - 4.7 -def GetDB(*, name, user, passwd, host='localhost', port=5432): 4.8 - """ 4.9 - На случай когда лениво каждый раз вызывать базу со всеми регалиями, 4.10 - регалии сохраняем в специального вызывателя :))) 4.11 - """ 4.12 - def func (): 4.13 - return DB(name, user, passwd, host=host, port=port) 4.14 - return func 4.15 - 4.16 -class DB: 4.17 - def __init__(self, db_name, db_user, db_passwd, *, host='localhost', port=5432): 4.18 - self._db = DBM.connect( 4.19 - host = host, 4.20 - port = port, 4.21 - user = db_user, 4.22 - password = db_passwd, 4.23 - database = db_name 4.24 - ) 4.25 - self.xact = self._db.xact 4.26 - self.q = self._db.query 4.27 - self.pq = self._db.prepare 4.28 - self.ex = self._db.execute 4.29 - self.proc = self._db.proc 4.30 - self.close = self._db.close 4.31 - 4.32 - def __del__(self): 4.33 - if self._db.state != 'closed': 4.34 - self._db.close() 4.35 \ No newline at end of file
5.1 --- a/awNet/db/sqlite.py Tue Nov 12 21:28:23 2019 +0300 5.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 5.3 @@ -1,55 +0,0 @@ 5.4 -# -*- coding: utf-8 -*- 5.5 -# Dep 5.6 -from sqlite3 import connect as _sqlite_connect 5.7 -# Exceptions 5.8 -from sqlite3 import Warning, Error, DatabaseError, IntegrityError, ProgrammingError 5.9 - 5.10 -# --- # PROCS # --- # 5.11 -def GetDB(db_file): 5.12 - def func(): 5.13 - return DB(db_file) 5.14 - return func 5.15 - 5.16 -# --- # CLASS # --- # 5.17 -class DB: 5.18 - def __init__(self, db_file): 5.19 - self._con = None 5.20 - self._con = _sqlite_connect(db_file) 5.21 - self.q = self._con.execute 5.22 - self.qm = self._con.executemany 5.23 - self.commit = self._con.commit 5.24 - self.rollback = self._con.rollback 5.25 - 5.26 - # DB PREP 5.27 - self.cq("PRAGMA journal=WAL") 5.28 - 5.29 - def __call__(self, *a, **wa): 5.30 - return self.q(*a, **wa) 5.31 - 5.32 - def __del__(self): 5.33 - if self._con == None: return 5.34 - try: 5.35 - self.rollback() 5.36 - self._con.close() 5.37 - except Error: 5.38 - pass 5.39 - except Warning: 5.40 - pass 5.41 - 5.42 - def cq(self, *a, **wa): 5.43 - try: 5.44 - res = self.q(*a, **wa) 5.45 - self.commit() 5.46 - return res 5.47 - except Exception as e: 5.48 - self.rollback() 5.49 - raise e 5.50 - 5.51 - def cqm(self, *a, **wa): 5.52 - try: 5.53 - res = self.qm(*a, **wa) 5.54 - self.commit() 5.55 - return res 5.56 - except Exception as e: 5.57 - self.rollback() 5.58 - raise e 5.59 \ No newline at end of file
7.1 --- a/awNet/webapp/tool/__init__.py Tue Nov 12 21:28:23 2019 +0300 7.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 7.3 @@ -1,4 +0,0 @@ 7.4 -# -*- coding: utf-8 -*- 7.5 -# --- 7.6 -# 7.7 -# --- 7.8 \ No newline at end of file
8.1 --- a/awNet/webapp/tool/url.py Tue Nov 12 21:28:23 2019 +0300 8.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 8.3 @@ -1,12 +0,0 @@ 8.4 -# -*- coding: utf-8 -*- 8.5 -# --- 8.6 -# Инструменты для работы с URL 8.7 -# --- 8.8 -from base64 import urlsafe_b64encode as _b64e, urlsafe_b64decode as _b64d 8.9 -from urllib.parse import quote as urlquote 8.10 - 8.11 -def encode(buf): 8.12 - return _b64e(buf.encode('UTF-8')).decode('UTF-8') 8.13 - 8.14 -def decode(buf): 8.15 - return _b64d(buf.encode('UTF-8')).decode('UTF-8')
9.1 --- a/awNet/webapp/win.py Tue Nov 12 21:28:23 2019 +0300 9.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 9.3 @@ -1,72 +0,0 @@ 9.4 -# -*- coding: utf-8 -*- 9.5 -# --- 9.6 -# Модуль среды приложений для Windows 9.7 -# --- 9.8 - 9.9 -from bottle import Bottle as App, request as Req, response as Ans, SimpleTemplate as BottleTemplate, abort as Abort 9.10 -from os.path import dirname, abspath as pAbs, split as pSplit, join as pJoin 9.11 - 9.12 -# --- CLASSES --- 9.13 -class ReqEnv: 9.14 - def __init__(self): 9.15 - self.urlBase = Req.environ.get('SCRIPT_NAME', '') 9.16 - 9.17 - def getUrl(self, url): 9.18 - url = str(url) 9.19 - buf = self.urlBase 9.20 - buf += url if url.startswith('/') else '/' + url 9.21 - return buf 9.22 - 9.23 - def __getitem__(self, key): 9.24 - return Req.environ[key] 9.25 - 9.26 - def __iter__(self): 9.27 - for key in Req.environ.keys(): 9.28 - yield key 9.29 - 9.30 - @staticmethod 9.31 - def abort(*a, **ka): 9.32 - Abort(*a, **ka) 9.33 - 9.34 - @staticmethod 9.35 - def get(self): 9.36 - return Req.GET 9.37 - 9.38 - @staticmethod 9.39 - def post(): 9.40 - return Req.POST 9.41 - 9.42 -class Env: 9.43 - def __init__(self, scFile): 9.44 - self.wd = pSplit(dirname(pAbs(scFile)))[0] 9.45 - self.static = pJoin(self.wd, 'static') 9.46 - 9.47 - def http(self, tplName): 9.48 - return BottleTemplate(name=pJoin(self.wd, 'http', '%s.html' % tplName)).render 9.49 - 9.50 - def db(self): 9.51 - """ 9.52 - Путь к базе SQLite, иные типы БД дожны иметь более 9.53 - серьёзные средства авторизации 9.54 - """ 9.55 - return pJoin(self.wd, 'db.sqlite') 9.56 - 9.57 - @staticmethod 9.58 - def httpTpl(buf): 9.59 - return BottleTemplate(buf).render 9.60 - 9.61 - @staticmethod 9.62 - def app(): 9.63 - return App() 9.64 - 9.65 - @staticmethod 9.66 - def appRun(app): 9.67 - app.run(server='cgi') 9.68 - 9.69 -# --- PROCS --- 9.70 -def runApp(app): 9.71 - Env.appRun(app) 9.72 - 9.73 -def Template(buf): 9.74 - return Env.httpTpl(buf) 9.75 - 9.76 \ No newline at end of file
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/db/ldap.py Fri Jul 24 14:40:37 2020 +0300 11.3 @@ -0,0 +1,84 @@ 11.4 +# coding: utf-8 11.5 + 11.6 +from ldap3 import Server, Connection, SIMPLE, SUBTREE 11.7 + 11.8 +LDAP_PAGE=1000 11.9 + 11.10 +class LdapError(Exception): pass 11.11 + 11.12 +class LdapRes(): 11.13 + def __init__(self, dn, attrib): 11.14 + self.dn = dn 11.15 + self.attr = attrib 11.16 + 11.17 + def __getitem__(self, item): 11.18 + return self.attr[item] 11.19 + 11.20 + def __iter__(self): 11.21 + return iter(self.attr) 11.22 + 11.23 + def __repr__(self): 11.24 + return '<LdapRes: dn: %s>' % self.dn 11.25 + 11.26 + @classmethod 11.27 + def fromLdapQuery(cls, q): 11.28 + if not isinstance(q, dict): 11.29 + raise LdapError('LdapRes: Parsing Error, not ldap response item') 11.30 + if not (('dn' in q) and ('attributes' in q)): 11.31 + raise LdapError('LdapRes: Parsing Error, format mismatch') 11.32 + 11.33 + return cls(q['dn'], q['attributes']) 11.34 + 11.35 +class Ldap(): 11.36 + def __init__(self, host, user, passwd, timeout=60, queryTimeout=300, **kwa): 11.37 + if 'baseDN' in kwa: 11.38 + self._baseDN = kwa['baseDN'] 11.39 + del kwa['baseDN'] 11.40 + else: 11.41 + self._baseDN = None 11.42 + ldapSrv = Server(host, connect_timeout=timeout, **kwa) 11.43 + self._conn = self._makeConnFabric(ldapSrv, authentication=SIMPLE, 11.44 + user=user, password=passwd, 11.45 + check_names=True, lazy=True, 11.46 + auto_referrals=False, raise_exceptions=True, auto_range=True 11.47 + ) 11.48 + self.queryTimeout = queryTimeout 11.49 + 11.50 + def __call__(self, filter, attrib, queryTimeout=None, baseDN = None): 11.51 + if baseDN is None: 11.52 + if self._baseDN is None: 11.53 + raise LdapError('No base dn on query execution') 11.54 + baseDN = self._baseDN 11.55 + if queryTimeout is None: 11.56 + queryTimeout = self.queryTimeout 11.57 + try: 11.58 + conn = self._conn() 11.59 + with conn: 11.60 + conn.open() 11.61 + conn.bind() 11.62 + 11.63 + res = conn.extend.standard.paged_search(baseDN, 11.64 + filter, attributes=attrib, paged_size=LDAP_PAGE, generator=False, 11.65 + search_scope=SUBTREE, time_limit=queryTimeout 11.66 + ) 11.67 + 11.68 + for i in res: 11.69 + if i['type'] == 'searchResEntry': 11.70 + yield LdapRes.fromLdapQuery(i) 11.71 + 11.72 + except Exception as e: 11.73 + raise LdapError("Error on get data (%s): %s" % (type(e), str(e)), *e.args[1:]) 11.74 + 11.75 + def getList(self, *a, **kwa): 11.76 + return [ i for i in self(*a, **kwa) ] 11.77 + 11.78 + @staticmethod 11.79 + def _makeConnFabric(*a, **kwa): 11.80 + def _func(): 11.81 + return Connection(*a, **kwa) 11.82 + 11.83 + return _func 11.84 + 11.85 + 11.86 + 11.87 + 11.88 \ No newline at end of file
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/db/pg/psy.py Fri Jul 24 14:40:37 2020 +0300 12.3 @@ -0,0 +1,69 @@ 12.4 +# -*- coding: utf-8 -*- 12.5 +import psycopg2 12.6 +from psycopg2 import Error as DBError, Warning, DataError, IntegrityError, ProgrammingError 12.7 +# DBError: Клас-родитель для всех ошибок 12.8 +# DataError: Проблема с данными (деление на ноль, численное изначение за областью значение и пр.) 12.9 +# IntegrityError: задеты ограничения целостности БД (уникольность, внешние ключи и пр.) 12.10 +# ProgrammingError: ошибки в запросах (таблица не найдена, или уже существует, ошибка в синтаксисе запроса и пр.) 12.11 + 12.12 +import psycopg2.extensions 12.13 +psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) 12.14 +psycopg2.extensions.register_type(psycopg2.extensions.UNICODEARRAY) 12.15 + 12.16 +class Error(Exception): pass 12.17 + 12.18 + 12.19 +def dbFactory(**kva): 12.20 + """ Фабрика объектов 12.21 + """ 12.22 + def _func(): 12.23 + return DB(**kva) 12.24 + return _func 12.25 + 12.26 +class DB(object): 12.27 + def __init__(self, **kva): 12.28 + self._conn = psycopg2.connect(**kva) 12.29 + self.commit = self._conn.commit 12.30 + self.rollback = self._conn.rollback 12.31 + self.cursor = self._conn.cursor 12.32 + self.reset = self._conn.reset 12.33 + 12.34 + def __enter__(self, *a, **kva): 12.35 + return self._conn.__enter__(*a, **kva) 12.36 + 12.37 + def __exit__(self, *a, **kva): 12.38 + return self._conn.__exit__(*a, **kva) 12.39 + 12.40 + def __call__(self, *a, **kva): 12.41 + _cur = self.cursor() 12.42 + _cur.execute(*a, **kva) 12.43 + return _cur 12.44 + 12.45 + def cq(self, *a, **kva): 12.46 + try: 12.47 + _cur = self.cursor() 12.48 + _cur.execute(*a, **kva) 12.49 + self.commit() 12.50 + return _cur 12.51 + except Exception as e: 12.52 + self.rollback() 12.53 + raise e 12.54 + 12.55 + def close(self): 12.56 + try: 12.57 + self.rollback() 12.58 + self._conn.close() 12.59 + except: 12.60 + pass 12.61 + 12.62 + def __del__(self): 12.63 + self.close() 12.64 + 12.65 + def __repr__(self): 12.66 + return 'DB(%s)' % self._conn.dsn 12.67 + 12.68 + 12.69 + 12.70 + 12.71 + 12.72 +
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 13.2 +++ b/db/pg/py.py Fri Jul 24 14:40:37 2020 +0300 13.3 @@ -0,0 +1,31 @@ 13.4 +import postgresql.driver as DBM 13.5 +import postgresql.exceptions as DBErr 13.6 + 13.7 +def GetDB(*, name, user, passwd, host='localhost', port=5432): 13.8 + """ 13.9 + На случай когда лениво каждый раз вызывать базу со всеми регалиями, 13.10 + регалии сохраняем в специального вызывателя :))) 13.11 + """ 13.12 + def func (): 13.13 + return DB(name, user, passwd, host=host, port=port) 13.14 + return func 13.15 + 13.16 +class DB: 13.17 + def __init__(self, db_name, db_user, db_passwd, *, host='localhost', port=5432): 13.18 + self._db = DBM.connect( 13.19 + host = host, 13.20 + port = port, 13.21 + user = db_user, 13.22 + password = db_passwd, 13.23 + database = db_name 13.24 + ) 13.25 + self.xact = self._db.xact 13.26 + self.q = self._db.query 13.27 + self.pq = self._db.prepare 13.28 + self.ex = self._db.execute 13.29 + self.proc = self._db.proc 13.30 + self.close = self._db.close 13.31 + 13.32 + def __del__(self): 13.33 + if self._db.state != 'closed': 13.34 + self._db.close() 13.35 \ No newline at end of file
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 14.2 +++ b/db/sqlite.py Fri Jul 24 14:40:37 2020 +0300 14.3 @@ -0,0 +1,34 @@ 14.4 +# coding: utf-8 14.5 +import sqlite3 14.6 + 14.7 +class DB: 14.8 + def __init__(self, db_file): 14.9 + self._con = sqlite3.connect(db_file) 14.10 + self._ex = self._con.execute 14.11 + self.commit = self._con.commit 14.12 + self.rollback = self._con.rollback 14.13 + 14.14 + # DB PREP 14.15 + self._ex("PRAGMA journal=WAL") 14.16 + self.commit() 14.17 + 14.18 + def __del__(self): 14.19 + try: 14.20 + self.rollback() 14.21 + self._con.close() 14.22 + except: 14.23 + pass 14.24 + 14.25 + def __call__(self, *a, **kwa): 14.26 + cur = self._con.cursor() 14.27 + cur.execute(*a, **kwa) 14.28 + return cur 14.29 + 14.30 + def cq(self, *a, **wa): 14.31 + try: 14.32 + res = self(*a, **wa) 14.33 + self.commit() 14.34 + return res 14.35 + except Exception as e: 14.36 + self.rollback() 14.37 + raise e
15.1 --- a/ldapUtil.py Tue Nov 12 21:28:23 2019 +0300 15.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 15.3 @@ -1,86 +0,0 @@ 15.4 -# coding: utf-8 15.5 -import ldap 15.6 - 15.7 -# =========================================================== 15.8 -# Статус завершения оперций 15.9 -STATUS_OK = 0 # Всё ОК. 15.10 -STATUS_BADAUTH = 1 # Не верные пользователь или пароль. 15.11 -STATUS_SERVERDOWN = 2 # Сервер не доступен. 15.12 -STATUS_SERVERERROR = 3 # Ошибка при взаимодействии с сервером. 15.13 - 15.14 -class LdapError(Exception): pass 15.15 - 15.16 -class LdapAuth(object): 15.17 - def __init__(self, server, userPrefix = 'CORP\\', baseDN = 'DC=example,DC=net', attr = []): 15.18 - self.server = server 15.19 - self.prefix = userPrefix 15.20 - self.status = '' 15.21 - self.baseDN = baseDN 15.22 - self.statusCode = 0 15.23 - self.groups = None 15.24 - self.attr = None 15.25 - self._needAttr = [ i for i in map(str, attr) ] 15.26 - 15.27 - def __call__(self, user, passwd): 15.28 - try: 15.29 - conn = ldap.initialize(self.server) 15.30 - conn.set_option(ldap.OPT_REFERRALS, 0) 15.31 - conn.simple_bind_s(self.prefix + user, passwd) 15.32 - except ldap.INVALID_CREDENTIALS: 15.33 - conn.unbind() 15.34 - self.status = 'Invalid credentials' 15.35 - self.statusCode = STATUS_BADAUTH 15.36 - return False 15.37 - except ldap.SERVER_DOWN: 15.38 - self.status = 'Server is down' 15.39 - self.statusCode = STATUS_SERVERDOWN 15.40 - return False 15.41 - 15.42 - self.groups = [] 15.43 - try: 15.44 - ldapData = conn.search_s(self.baseDN, ldap.SCOPE_SUBTREE, '(cn=%s)' % user, ['memberOf'] + self._needAttr)[0][1] 15.45 - for i in ldapData['memberOf']: 15.46 - self.groups.append(i.split(',')[0].split('=')[1].decode('utf-8')) 15.47 - del ldapData['memberOf'] 15.48 - self.attr = ldapData 15.49 - except KeyError: 15.50 - self.status = 'User object from LDAP is wrong, it can be anonymous logon' 15.51 - self.statusCode = STATUS_SERVERERROR 15.52 - return False 15.53 - finally: 15.54 - conn.unbind() 15.55 - 15.56 - return True 15.57 - 15.58 - def __getitem__(self, key): 15.59 - return self.attr[key] 15.60 - 15.61 - def memberOf(self, group): 15.62 - """Проверка на присутствие у пользователя некоторой группы 15.63 - """ 15.64 - if self.groups == None: 15.65 - raise LdapError('Request membership before auth call.') 15.66 - 15.67 - if not isinstance(group, unicode): 15.68 - if isinstance(group, str): 15.69 - group = group.decode('utf-8') 15.70 - else: 15.71 - group = str(group).decode('utf-8') 15.72 - 15.73 - if group in self.groups: 15.74 - return True 15.75 - else: 15.76 - return False 15.77 - 15.78 - def __contains__(self, group): 15.79 - return self.memberOf(group) 15.80 - 15.81 - def memberOfGroups(self, groups): 15.82 - if not len(groups) > 0: 15.83 - return False 15.84 - 15.85 - for group in groups: 15.86 - if not group in self: 15.87 - return False 15.88 - 15.89 - return True
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 16.2 +++ b/ldap_utils/ldap_passwd_changer.py Fri Jul 24 14:40:37 2020 +0300 16.3 @@ -0,0 +1,111 @@ 16.4 +# coding: utf-8 16.5 +""" 16.6 +Смена пароля на пользователя в AD посредством Python 16.7 + 16.8 +""" 16.9 + 16.10 +from ldap3 import Server, Connection, SIMPLE, SUBTREE 16.11 +from ldap3.core.exceptions import LDAPException 16.12 +from secrets import token_urlsafe 16.13 +import string 16.14 + 16.15 +class LdapError(Exception): 16.16 + @staticmethod 16.17 + def errFmt(e): 16.18 + _buf = ' '.join(str(e).split()) # всё в одну строку с одним пробелом 16.19 + # между словами 16.20 + return "%s(%s)" % (type(e).__name__, _buf) 16.21 + 16.22 +class LdapServerError(LdapError): pass 16.23 +class LdapPassGenError(LdapError): pass 16.24 +class LdapNotFound(LdapError): pass 16.25 + 16.26 + 16.27 +class LdapPasswdChanger(object): 16.28 + def __init__(self, ldap_server_uri, ldap_user, ldap_passwd, ldap_baseDN, ldap_timeout=120, ldap_reconnect=5, passwd_len=25): 16.29 + self.ldap_server_uri = ldap_server_uri # URI к серверу: ldaps://server_name:port_number 16.30 + self.ldap_user = ldap_user # Пользователь, имеющий право сброса паролей 16.31 + self.ldap_passwd = ldap_passwd # Пароль к тому пользователю 16.32 + self.ldap_baseDN = ldap_baseDN 16.33 + self.ldap_timeout = ldap_timeout # Таймаут операций с сервером 16.34 + self.passwd_len = passwd_len # Желаемая длинна пароля 16.35 + self.ldap_reconnect = ldap_reconnect # Количество попыток переподключения к серверу 16.36 + 16.37 + # Заготовка под пароли 16.38 + self._set_punct = set(string.punctuation) 16.39 + self._set_uper = set(string.ascii_uppercase) 16.40 + self._set_lower = set(string.ascii_lowercase) 16.41 + 16.42 + def getPasswd(self): 16.43 + _cnt = 0 16.44 + while True: 16.45 + res = token_urlsafe(self.passwd_len) 16.46 + _res_set = set(res) 16.47 + if ((_res_set & self._set_punct) 16.48 + and (_res_set & self._set_uper) 16.49 + and (_res_set & self._set_lower) 16.50 + ): 16.51 + return res 16.52 + 16.53 + _cnt += 1 16.54 + if _cnt > 60: 16.55 + raise LdapPassGenError('Не удалось сгенерировать пароль') 16.56 + 16.57 + 16.58 + def changePass(self, login): 16.59 + _reconnects = self.ldap_reconnect 16.60 + if _reconnects is None or _reconnects < 1: 16.61 + _reconnects = 1 16.62 + 16.63 + ldapConn = None 16.64 + while True: 16.65 + _reconnects -= 1 16.66 + try: 16.67 + ldapSrv = Server(self.ldap_server_uri, connect_timeout=self.ldap_timeout) 16.68 + ldapConn = Connection(ldapSrv, authentication=SIMPLE, 16.69 + user=self.ldap_user, password=self.ldap_passwd, 16.70 + check_names=True, 16.71 + auto_referrals=False, raise_exceptions=True, auto_range=True, 16.72 + ) 16.73 + ldapConn.open() 16.74 + if not ldapConn.bind(): 16.75 + continue 16.76 + 16.77 + break 16.78 + except LDAPException as e: 16.79 + if not _reconnects > 0: 16.80 + raise LdapServerError('Ошибка подключения к серверу: %s' % LdapServerError.errFmt(e)) 16.81 + 16.82 + filter = '(sAMAccountName=%s)' % login 16.83 + try: 16.84 + if not ldapConn.search(self.ldap_baseDN, filter): 16.85 + raise LdapServerError('Сервер вернул статус ощибки: %(desc)s(%(code)s) - %(msg)s' % { 16.86 + 'desc': ldapConn.result['description'], 16.87 + 'code': ldapConn.result['result'], 16.88 + 'msg': ldapConn.result['message'], 16.89 + }) 16.90 + except LDAPException as e: 16.91 + raise LdapServerError('Ошибка при поиска УЗ в ldap: %s' % LdapServerError.errFmt(e)) 16.92 + 16.93 + if not len(ldapConn.entries) > 0: 16.94 + raise LdapNotFound('Не найдена учётная запись: %s' % login) 16.95 + elif len(ldapConn.entries) != 1: 16.96 + raise LdapError('Наёдено более одной УЗ по заданному имени %s: %s - %s' %( 16.97 + login, 16.98 + len(ldapConn.entries), 16.99 + '; '.join([i.entry_dn for i in ldapConn.entries[:10]]) 16.100 + )) 16.101 + 16.102 + login_dn = ldapConn.entries[0].entry_dn 16.103 + new_passwd = self.getPasswd() 16.104 + try: 16.105 + ldapConn.extend.microsoft.modify_password(login_dn, new_password=new_passwd, old_password=None) 16.106 + except LDAPException as e: 16.107 + raise LdapServerError('Ошибка при смене пароля на УЗ: %s' % LdapServerError.errFmt(e)) 16.108 + 16.109 + return new_passwd 16.110 + 16.111 + 16.112 + 16.113 + 16.114 +
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 17.2 +++ b/ldap_utils/ldap_util.py Fri Jul 24 14:40:37 2020 +0300 17.3 @@ -0,0 +1,86 @@ 17.4 +# coding: utf-8 17.5 +import ldap 17.6 + 17.7 +# =========================================================== 17.8 +# Статус завершения оперций 17.9 +STATUS_OK = 0 # Всё ОК. 17.10 +STATUS_BADAUTH = 1 # Не верные пользователь или пароль. 17.11 +STATUS_SERVERDOWN = 2 # Сервер не доступен. 17.12 +STATUS_SERVERERROR = 3 # Ошибка при взаимодействии с сервером. 17.13 + 17.14 +class LdapError(Exception): pass 17.15 + 17.16 +class LdapAuth(object): 17.17 + def __init__(self, server, userPrefix = 'CORP\\', baseDN = 'DC=example,DC=net', attr = []): 17.18 + self.server = server 17.19 + self.prefix = userPrefix 17.20 + self.status = '' 17.21 + self.baseDN = baseDN 17.22 + self.statusCode = 0 17.23 + self.groups = None 17.24 + self.attr = None 17.25 + self._needAttr = [ i for i in map(str, attr) ] 17.26 + 17.27 + def __call__(self, user, passwd): 17.28 + try: 17.29 + conn = ldap.initialize(self.server) 17.30 + conn.set_option(ldap.OPT_REFERRALS, 0) 17.31 + conn.simple_bind_s(self.prefix + user, passwd) 17.32 + except ldap.INVALID_CREDENTIALS: 17.33 + conn.unbind() 17.34 + self.status = 'Invalid credentials' 17.35 + self.statusCode = STATUS_BADAUTH 17.36 + return False 17.37 + except ldap.SERVER_DOWN: 17.38 + self.status = 'Server is down' 17.39 + self.statusCode = STATUS_SERVERDOWN 17.40 + return False 17.41 + 17.42 + self.groups = [] 17.43 + try: 17.44 + ldapData = conn.search_s(self.baseDN, ldap.SCOPE_SUBTREE, '(cn=%s)' % user, ['memberOf'] + self._needAttr)[0][1] 17.45 + for i in ldapData['memberOf']: 17.46 + self.groups.append(i.split(',')[0].split('=')[1].decode('utf-8')) 17.47 + del ldapData['memberOf'] 17.48 + self.attr = ldapData 17.49 + except KeyError: 17.50 + self.status = 'User object from LDAP is wrong, it can be anonymous logon' 17.51 + self.statusCode = STATUS_SERVERERROR 17.52 + return False 17.53 + finally: 17.54 + conn.unbind() 17.55 + 17.56 + return True 17.57 + 17.58 + def __getitem__(self, key): 17.59 + return self.attr[key] 17.60 + 17.61 + def memberOf(self, group): 17.62 + """Проверка на присутствие у пользователя некоторой группы 17.63 + """ 17.64 + if self.groups == None: 17.65 + raise LdapError('Request membership before auth call.') 17.66 + 17.67 + if not isinstance(group, unicode): 17.68 + if isinstance(group, str): 17.69 + group = group.decode('utf-8') 17.70 + else: 17.71 + group = str(group).decode('utf-8') 17.72 + 17.73 + if group in self.groups: 17.74 + return True 17.75 + else: 17.76 + return False 17.77 + 17.78 + def __contains__(self, group): 17.79 + return self.memberOf(group) 17.80 + 17.81 + def memberOfGroups(self, groups): 17.82 + if not len(groups) > 0: 17.83 + return False 17.84 + 17.85 + for group in groups: 17.86 + if not group in self: 17.87 + return False 17.88 + 17.89 + return True
18.1 --- a/log/slog_syslogger.py Tue Nov 12 21:28:23 2019 +0300 18.2 +++ b/log/slog_syslogger.py Fri Jul 24 14:40:37 2020 +0300 18.3 @@ -1,99 +1,145 @@ 18.4 -# coding: utf-8 18.5 -""" 18.6 -Тривиальная реализация сислоггера без блэк-джека и поэтесс 18.7 - 18.8 -Метки в журнале о уровне сообщения: 18.9 - "`": Debug 18.10 - ".": Info 18.11 - "*": Warning 18.12 - "!": Error 18.13 - "#": Alert 18.14 - 18.15 -""" 18.16 -from time import time 18.17 -from datetime import timedelta 18.18 - 18.19 -import syslog 18.20 -from traceback import extract_tb, extract_stack 18.21 -from sys import exc_info 18.22 - 18.23 - 18.24 -class Timing(object): 18.25 - def __init__(self, name=None): 18.26 - if name is None: 18.27 - self.prefix = '' 18.28 - else: 18.29 - self.prefix = '%s :: ' % name 18.30 - self.tsAll = time() 18.31 - self.ts = self.tsAll 18.32 - 18.33 - def getTime(self): 18.34 - return time() - self.ts 18.35 - 18.36 - def reset(self): 18.37 - self.ts = time() 18.38 - self.tsAll = self.ts 18.39 - 18.40 - def __str__(self): 18.41 - ts = time() 18.42 - return self.prefix + '%s(%.4f)' % (timedelta(seconds=(ts - self.tsAll)), ts - self.ts) 18.43 - 18.44 - def __call__(self, msg): 18.45 - _buf = '%s | %s' % (self, msg) 18.46 - self.ts = time() 18.47 - return _buf 18.48 - 18.49 - 18.50 -class SimpleSysLogger(object): 18.51 - @staticmethod 18.52 - def initSyslog(ident): 18.53 - syslog.openlog(ident, syslog.LOG_PID) 18.54 - 18.55 - @staticmethod 18.56 - def getTiming(name=None): 18.57 - return Timing(name) 18.58 - 18.59 - def __init__(self, prefix, facility=syslog.LOG_USER): 18.60 - self.prefix = str(prefix) 18.61 - self.facility = facility 18.62 - 18.63 - def _write(self, flag, mark, msg): 18.64 - for l in str(msg).splitlines(): 18.65 - syslog.syslog(self.facility | flag, '%s: %s %s' % (self.prefix, mark, l)) 18.66 - 18.67 - def __call__(self, msg): 18.68 - self._write(syslog.LOG_INFO, '.', msg) 18.69 - 18.70 - def err(self, msg): 18.71 - self._write(syslog.LOG_ERR, '!', msg) 18.72 - 18.73 - def warn(self, msg): 18.74 - self._write(syslog.LOG_WARNING, '*', msg) 18.75 - 18.76 - def debug(self, msg): 18.77 - self._write(syslog.LOG_DEBUG, '`', msg) 18.78 - 18.79 - def alert(self, msg): 18.80 - self._write(syslog.LOG_ALERT, '#', msg) 18.81 - 18.82 - def sublog(self, prefix): 18.83 - return self.__class__('%s/%s' % (self.prefix, prefix), self.facility) 18.84 - 18.85 - def excpt(self, msg, eClass=None, eObj=None, eTb=None, stack_skip=0): 18.86 - if eClass is None: 18.87 - eClass, eObj, eTb = exc_info() 18.88 - 18.89 - tbData_tb = list(extract_tb(eTb))[::-1] 18.90 - tbData_stack = list(extract_stack())[::-1][(2 + stack_skip):] 18.91 - self.err(msg) 18.92 - self.err('--- EXCEPTION ---') 18.93 - self.err(' %s (%s)' % (eClass.__name__, eObj)) 18.94 - self.err('--- TRACEBACK ---') 18.95 - for _tbFile, _tbLine, _tbFunc, _tbText in tbData_tb: 18.96 - self.err('File: %s, line %s in %s' % (_tbFile, _tbLine, _tbFunc)) 18.97 - self.err(' %s' % _tbText) 18.98 - self.err('>>> Exception Handler <<<') 18.99 - for _tbFile, _tbLine, _tbFunc, _tbText in tbData_stack: 18.100 - self.err('File: %s, line %s in %s' % (_tbFile, _tbLine, _tbFunc)) 18.101 - self.err(' %s' % _tbText) 18.102 - self.err('--- END EXCEPTION ---') 18.103 +# coding: utf-8 18.104 + 18.105 +"""\ 18.106 +Логирование в системный журнал Unix 18.107 + 18.108 +Метки в журнале о уровне сообщения: 18.109 + "`": Debug 18.110 + ".": Info 18.111 + "*": Warning 18.112 + "!": Error 18.113 + "#": Alert 18.114 +""" 18.115 + 18.116 +import syslog 18.117 +from sys import exc_info 18.118 +from time import time 18.119 +from traceback import extract_tb, extract_stack 18.120 +from datetime import timedelta 18.121 + 18.122 +LOG_FACILITY = { 18.123 + 'auth': syslog.LOG_AUTH, 18.124 + 'authpriv': syslog.LOG_AUTH, 18.125 + 'cron': syslog.LOG_CRON, 18.126 + 'daemon': syslog.LOG_DAEMON, 18.127 + 'ftp': syslog.LOG_DAEMON, 18.128 + 'kern': syslog.LOG_KERN, 18.129 + 'lpr': syslog.LOG_LPR, 18.130 + 'mail': syslog.LOG_MAIL, 18.131 + 'news': syslog.LOG_NEWS, 18.132 + 'syslog': syslog.LOG_SYSLOG, 18.133 + 'user': syslog.LOG_USER, 18.134 + 'uucp': syslog.LOG_UUCP, 18.135 + 'local0': syslog.LOG_LOCAL0, 18.136 + 'local1': syslog.LOG_LOCAL1, 18.137 + 'local2': syslog.LOG_LOCAL2, 18.138 + 'local3': syslog.LOG_LOCAL3, 18.139 + 'local4': syslog.LOG_LOCAL4, 18.140 + 'local5': syslog.LOG_LOCAL5, 18.141 + 'local6': syslog.LOG_LOCAL6, 18.142 + 'local7': syslog.LOG_LOCAL7 18.143 +} 18.144 + 18.145 +class LoggerError(Exception): pass 18.146 + 18.147 + 18.148 +# --- INTERFACE --- # 18.149 +FACILITY = LOG_FACILITY['user'] 18.150 + 18.151 + 18.152 +def log_prep(ident, facility='user'): 18.153 + global FACILITY 18.154 + if not facility.lower() in LOG_FACILITY: 18.155 + raise LoggerError('Unknown facility') 18.156 + 18.157 + syslog.openlog(ident, syslog.LOG_PID) 18.158 + 18.159 + FACILITY = LOG_FACILITY[facility] 18.160 + 18.161 + 18.162 +class Timing(object): 18.163 + def __init__(self, name=None): 18.164 + if name is None: 18.165 + self.prefix = '' 18.166 + else: 18.167 + self.prefix = '%s :: ' % name 18.168 + self.tsAll = time() 18.169 + self.ts = self.tsAll 18.170 + 18.171 + def getTime(self): 18.172 + return time() - self.ts 18.173 + 18.174 + def reset(self): 18.175 + self.ts = time() 18.176 + self.tsAll = self.ts 18.177 + 18.178 + def __str__(self): 18.179 + ts = time() 18.180 + return self.prefix + '%s(%.4f)' % (timedelta(seconds=(ts - self.tsAll)), ts - self.ts) 18.181 + 18.182 + def __call__(self, msg): 18.183 + _buf = '%s | %s' % (self, msg) 18.184 + self.ts = time() 18.185 + return _buf 18.186 + 18.187 + 18.188 +class SysLogger(object): 18.189 + init_log = log_prep 18.190 + 18.191 + @staticmethod 18.192 + def get_timing(name=None): 18.193 + return Timing(name) 18.194 + 18.195 + def __init__(self, prefix, facility=FACILITY): 18.196 + self.prefix = str(prefix) 18.197 + self.facility = facility 18.198 + 18.199 + def _write(self, flag, mark, msg): 18.200 + for l in str(msg).splitlines(): 18.201 + syslog.syslog(self.facility | flag, '%s: %s %s' % (self.prefix, mark, l)) 18.202 + 18.203 + def __call__(self, msg): 18.204 + self._write(syslog.LOG_INFO, '.', msg) 18.205 + 18.206 + def err(self, msg): 18.207 + self._write(syslog.LOG_ERR, '!', msg) 18.208 + 18.209 + def warn(self, msg): 18.210 + self._write(syslog.LOG_WARNING, '*', msg) 18.211 + 18.212 + def debug(self, msg): 18.213 + self._write(syslog.LOG_DEBUG, '`', msg) 18.214 + 18.215 + def alert(self, msg): 18.216 + self._write(syslog.LOG_ALERT, '#', msg) 18.217 + 18.218 + def sublog(self, prefix): 18.219 + return self.__class__('%s/%s' % (self.prefix, prefix), self.facility) 18.220 + 18.221 + def excpt(self, msg, eClass=None, eObj=None, eTb=None, stack_skip=0): 18.222 + if eClass is None: 18.223 + eClass, eObj, eTb = exc_info() 18.224 + 18.225 + if eClass is None: 18.226 + # Если вдруг вызываем без произошедшего исключения 18.227 + self.err(msg) 18.228 + else: 18.229 + tbData_tb = list(extract_tb(eTb))[::-1] 18.230 + tbData_stack = list(extract_stack())[::-1][(2 + stack_skip):] 18.231 + 18.232 + self.err(msg) 18.233 + 18.234 + self.err('--- EXCEPTION ---') 18.235 + self.err(' %s (%s)' % (eClass.__name__, eObj)) 18.236 + 18.237 + self.err('--- TRACEBACK ---') 18.238 + for _tbFile, _tbLine, _tbFunc, _tbText in tbData_tb: 18.239 + self.err('File: %s, line %s in %s' % (_tbFile, _tbLine, _tbFunc)) 18.240 + self.err(' %s' % _tbText) 18.241 + 18.242 + self.err('>>> Exception Handler <<<') 18.243 + for _tbFile, _tbLine, _tbFunc, _tbText in tbData_stack: 18.244 + self.err('File: %s, line %s in %s' % (_tbFile, _tbLine, _tbFunc)) 18.245 + self.err(' %s' % _tbText) 18.246 + 18.247 + self.err('--- END EXCEPTION ---')
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 19.2 +++ b/log/slog_syslogger_tiny.py Fri Jul 24 14:40:37 2020 +0300 19.3 @@ -0,0 +1,99 @@ 19.4 +# coding: utf-8 19.5 +""" 19.6 +Тривиальная реализация сислоггера без блэк-джека и поэтесс 19.7 + 19.8 +Метки в журнале о уровне сообщения: 19.9 + "`": Debug 19.10 + ".": Info 19.11 + "*": Warning 19.12 + "!": Error 19.13 + "#": Alert 19.14 + 19.15 +""" 19.16 +from time import time 19.17 +from datetime import timedelta 19.18 + 19.19 +import syslog 19.20 +from traceback import extract_tb, extract_stack 19.21 +from sys import exc_info 19.22 + 19.23 + 19.24 +class Timing(object): 19.25 + def __init__(self, name=None): 19.26 + if name is None: 19.27 + self.prefix = '' 19.28 + else: 19.29 + self.prefix = '%s :: ' % name 19.30 + self.tsAll = time() 19.31 + self.ts = self.tsAll 19.32 + 19.33 + def getTime(self): 19.34 + return time() - self.ts 19.35 + 19.36 + def reset(self): 19.37 + self.ts = time() 19.38 + self.tsAll = self.ts 19.39 + 19.40 + def __str__(self): 19.41 + ts = time() 19.42 + return self.prefix + '%s(%.4f)' % (timedelta(seconds=(ts - self.tsAll)), ts - self.ts) 19.43 + 19.44 + def __call__(self, msg): 19.45 + _buf = '%s | %s' % (self, msg) 19.46 + self.ts = time() 19.47 + return _buf 19.48 + 19.49 + 19.50 +class SimpleSysLogger(object): 19.51 + @staticmethod 19.52 + def initSyslog(ident): 19.53 + syslog.openlog(ident, syslog.LOG_PID) 19.54 + 19.55 + @staticmethod 19.56 + def getTiming(name=None): 19.57 + return Timing(name) 19.58 + 19.59 + def __init__(self, prefix, facility=syslog.LOG_USER): 19.60 + self.prefix = str(prefix) 19.61 + self.facility = facility 19.62 + 19.63 + def _write(self, flag, mark, msg): 19.64 + for l in str(msg).splitlines(): 19.65 + syslog.syslog(self.facility | flag, '%s: %s %s' % (self.prefix, mark, l)) 19.66 + 19.67 + def __call__(self, msg): 19.68 + self._write(syslog.LOG_INFO, '.', msg) 19.69 + 19.70 + def err(self, msg): 19.71 + self._write(syslog.LOG_ERR, '!', msg) 19.72 + 19.73 + def warn(self, msg): 19.74 + self._write(syslog.LOG_WARNING, '*', msg) 19.75 + 19.76 + def debug(self, msg): 19.77 + self._write(syslog.LOG_DEBUG, '`', msg) 19.78 + 19.79 + def alert(self, msg): 19.80 + self._write(syslog.LOG_ALERT, '#', msg) 19.81 + 19.82 + def sublog(self, prefix): 19.83 + return self.__class__('%s/%s' % (self.prefix, prefix), self.facility) 19.84 + 19.85 + def excpt(self, msg, eClass=None, eObj=None, eTb=None, stack_skip=0): 19.86 + if eClass is None: 19.87 + eClass, eObj, eTb = exc_info() 19.88 + 19.89 + tbData_tb = list(extract_tb(eTb))[::-1] 19.90 + tbData_stack = list(extract_stack())[::-1][(2 + stack_skip):] 19.91 + self.err(msg) 19.92 + self.err('--- EXCEPTION ---') 19.93 + self.err(' %s (%s)' % (eClass.__name__, eObj)) 19.94 + self.err('--- TRACEBACK ---') 19.95 + for _tbFile, _tbLine, _tbFunc, _tbText in tbData_tb: 19.96 + self.err('File: %s, line %s in %s' % (_tbFile, _tbLine, _tbFunc)) 19.97 + self.err(' %s' % _tbText) 19.98 + self.err('>>> Exception Handler <<<') 19.99 + for _tbFile, _tbLine, _tbFunc, _tbText in tbData_stack: 19.100 + self.err('File: %s, line %s in %s' % (_tbFile, _tbLine, _tbFunc)) 19.101 + self.err(' %s' % _tbText) 19.102 + self.err('--- END EXCEPTION ---')
20.1 --- a/log/syslogger.py Tue Nov 12 21:28:23 2019 +0300 20.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 20.3 @@ -1,278 +0,0 @@ 20.4 -#!/usr/bin/env python 20.5 -# -*- coding: utf-8 -*- 20.6 -""" Логирование в системный журнал Unix 20.7 - 20.8 -Метки в журнале о уровне сообщения: 20.9 - "`": Debug 20.10 - ".": Info 20.11 - "*": Warning 20.12 - "!": Error 20.13 - "#": Alert 20.14 -""" 20.15 -import syslog 20.16 -from sys import argv, version_info, exc_info 20.17 -from time import time 20.18 -from traceback import extract_tb 20.19 - 20.20 -LOG_FACILITY = { 20.21 - 'auth': syslog.LOG_AUTH, 20.22 - 'authpriv': syslog.LOG_AUTH, 20.23 - 'cron': syslog.LOG_CRON, 20.24 - 'daemon': syslog.LOG_DAEMON, 20.25 - 'ftp': syslog.LOG_DAEMON, 20.26 - 'kern': syslog.LOG_KERN, 20.27 - 'lpr': syslog.LOG_LPR, 20.28 - 'mail': syslog.LOG_MAIL, 20.29 - 'news': syslog.LOG_NEWS, 20.30 - 'syslog': syslog.LOG_SYSLOG, 20.31 - 'user': syslog.LOG_USER, 20.32 - 'uucp': syslog.LOG_UUCP, 20.33 - 'local0': syslog.LOG_LOCAL0, 20.34 - 'local1': syslog.LOG_LOCAL1, 20.35 - 'local2': syslog.LOG_LOCAL2, 20.36 - 'local3': syslog.LOG_LOCAL3, 20.37 - 'local4': syslog.LOG_LOCAL4, 20.38 - 'local5': syslog.LOG_LOCAL5, 20.39 - 'local6': syslog.LOG_LOCAL6, 20.40 - 'local7': syslog.LOG_LOCAL7 20.41 -} 20.42 - 20.43 -# --- INTERFACE --- # 20.44 -FACILITY = LOG_FACILITY['user'] 20.45 -def logPrep(ident, facility='user'): 20.46 - global FACILITY 20.47 - if not facility.lower() in LOG_FACILITY: 20.48 - raise LoggerError('Unknown facility') 20.49 - syslog.openlog(ident, syslog.LOG_PID) 20.50 - 20.51 - FACILITY = LOG_FACILITY[facility] 20.52 - 20.53 - 20.54 -# --- ABSTRACT PART --- # 20.55 - 20.56 -LOG_DEBUG = "`" 20.57 -LOG_INFO = "." 20.58 -LOG_WARN = "*" 20.59 -LOG_ERR = "!" 20.60 -LOG_ALERT = "#" 20.61 - 20.62 -class LoggerError(Exception): pass 20.63 - 20.64 -if version_info.major == 3: 20.65 - def _strWrap(msg): 20.66 - return str(msg) 20.67 -elif version_info.major == 2: 20.68 - def _strWrap(msg): 20.69 - if isinstance(msg, unicode): 20.70 - return msg 20.71 - else: 20.72 - return str(msg).decode('utf-8') 20.73 -else: 20.74 - raise LoggerError('Unknown major version of Python') 20.75 - 20.76 - 20.77 -def _splitLines(obj): 20.78 - if isinstance(obj, (list, tuple)): 20.79 - for i in _parseList(obj): 20.80 - yield i 20.81 - elif isinstance(obj, dict): 20.82 - for i in _parseDict(obj): 20.83 - yield i 20.84 - else: 20.85 - buf = _strWrap(obj).splitlines() 20.86 - for i in buf: 20.87 - if len(i) < 401: 20.88 - yield i 20.89 - else: 20.90 - i = '<| ' + i + ' |>' 20.91 - lenI = len(i) 20.92 - c = 0 20.93 - while (c + 401) < lenI: 20.94 - yield i[c:c+401] 20.95 - c += 401 20.96 - yield i[c:] 20.97 - 20.98 -def _parseList(lst): 20.99 - for i in lst: 20.100 - i = _splitLines(i) 20.101 - try: 20.102 - yield '- %s' % next(i) 20.103 - for l in i: 20.104 - yield ' %s' % l 20.105 - except StopIteration: pass 20.106 - 20.107 -def _parseDict(dct): 20.108 - for key in sorted(dct): 20.109 - yield '%s:' % key 20.110 - for l in _splitLines(dct[key]): 20.111 - yield ' %s' % l 20.112 - 20.113 -def _parseArgs(a, kwa): 20.114 - if a: 20.115 - yield '| --- ARGS:' 20.116 - for i in _parseList(a): 20.117 - yield '| %s' % i 20.118 - 20.119 - if kwa: 20.120 - yield '| --- NAMED ARGS:' 20.121 - for i in _parseDict(kwa): 20.122 - yield '| %s' % i 20.123 - 20.124 - if a or kwa: 20.125 - yield '| ---' 20.126 - 20.127 -class TimingLog(): 20.128 - def __init__(self, logproc, opName): 20.129 - self.logproc = logproc 20.130 - self.opName = opName 20.131 - self.dt = time() 20.132 - 20.133 - def __str__(self): 20.134 - return '%s :: %.4f' % (self.opName, (time() - self.dt)) 20.135 - 20.136 - def __call__(self, msg, *a, **kwa): 20.137 - self.logproc('%s | %s' % (self, msg)) 20.138 - for i in _parseArgs(a, kwa): 20.139 - self.logproc('%s | %s' % (self, i)) 20.140 - 20.141 - def reset(self): 20.142 - self.dt = time() 20.143 - 20.144 - 20.145 -class OperationContext(object): 20.146 - def __init__(self, log, opName): 20.147 - self.opName = opName 20.148 - self.log = log 20.149 - self.msgBuf = [] 20.150 - 20.151 - def __call__(self, msg, *a, **kwa): 20.152 - self.msgBuf.append((msg, [ i for i in map(_strWrap, a)])) 20.153 - 20.154 - def __enter__(self): 20.155 - return self 20.156 - 20.157 - def __exit__(self, exc_type, exc_val, exc_tb): 20.158 - if exc_type is not None: 20.159 - self.log.err("Operation '%s' fail" % self.opName) 20.160 - self.log.excpt_handler(exc_type, exc_val, exc_tb) 20.161 - 20.162 - if self.msgBuf: 20.163 - self.log.err('--- MESSAGES ---') 20.164 - for msg, a, kwa in self.msgBuf: 20.165 - self.log.err(msg, a) 20.166 - self.log.err('--- END MESSAGES ---') 20.167 - 20.168 - 20.169 -class AbstractLogger(object): 20.170 - def err(self, msg, *a, **kwa): 20.171 - raise LoggerError('NotImplemented') 20.172 - 20.173 - def __call__(self, msg, *a, **kwa): 20.174 - raise LoggerError('NotImplemented') 20.175 - 20.176 - def excpt_handler(self, eType, eObj, eTb): 20.177 - eTb = extract_tb(eTb) 20.178 - eType = str(eType) 20.179 - try: 20.180 - eType = eType.split("'")[1] 20.181 - except IndexError: 20.182 - pass 20.183 - 20.184 - try: 20.185 - eArgs = eObj.args[1:] 20.186 - except: 20.187 - eArgs = [] 20.188 - 20.189 - try: 20.190 - eKwa = eObj.kwa 20.191 - except: 20.192 - eKwa = {} 20.193 - 20.194 - eObj = str(eObj) 20.195 - 20.196 - self.err('--- EXCEPTION ---') 20.197 - self.err(eType) 20.198 - 20.199 - for l in _splitLines(eObj): 20.200 - self.err(' ' + l) 20.201 - 20.202 - for l in _parseArgs(eArgs, eKwa): 20.203 - self.err(l) 20.204 - 20.205 - self.err('--- TRACEBACK ---') 20.206 - for _tbFile, _tbLine, _tbFunc, _tbText in eTb: 20.207 - self.err('File: %s, line %s in %s' % (_tbFile, _tbLine, _tbFunc)) 20.208 - self.err(' %s' % _tbText) 20.209 - self.err('--- END EXCEPTION ---') 20.210 - 20.211 - def excpt(self, msg, *a, **kwa): 20.212 - eType, eObj, eTb = exc_info() 20.213 - self.err(msg, *a, **kwa) 20.214 - self.excpt_handler(eType, eObj, eTb) 20.215 - 20.216 - def cntxt(self, opName): 20.217 - """ 20.218 - Если операцию нужно залогировать но при этом совершенно не хочется покрывать код 20.219 - толстым слоем try ... except можно завернуть операцию в контекст, и даже сохранить 20.220 - возвращаемом логером объекте отладочную информацию. При падении и только при нём 20.221 - это обязательно попадёт в лог. 20.222 - 20.223 - ОСТОРОЖНО ПАМЯТЬ!!!!! 20.224 - Объект копирует себе все переданные данные и опреобразует их сразу в строки 20.225 - Если объекту передать одни и те же параметры они всё равно будут храниться 20.226 - по копии на каждый вызов в виде строк, что может сьесть много памяти и вызвать 20.227 - болезненное её особождение. 20.228 - 20.229 - :param opName: членораздельное имя операции 20.230 - """ 20.231 - return OperationContext(self, opName) 20.232 - 20.233 - def getTiming(self, opName): 20.234 - return TimingLog(self, opName) 20.235 - 20.236 - def dumpDict(self, msg, dct): 20.237 - self('--- ' + _strWrap(msg)) 20.238 - for i in _parseDict(dct): 20.239 - self('| ' + i) 20.240 - self('---') 20.241 - 20.242 - @staticmethod 20.243 - def logPrep(*a, **kwa): 20.244 - logPrep(*a, **kwa) 20.245 - 20.246 -# --- END ABSTRACT PART --- # 20.247 - 20.248 -class Logger(AbstractLogger): 20.249 - facility = FACILITY 20.250 - def __init__(self, modName = 'main'): 20.251 - self.name = modName 20.252 - 20.253 - def _write(self, flags, prefix, msg): 20.254 - syslog.syslog(flags, u"%s %s: %s" % (prefix, self.name, msg)) 20.255 - 20.256 - def __call__(self, msg, *a, **kwa): 20.257 - _flags = self.facility | syslog.LOG_INFO 20.258 - _prefix = LOG_INFO + ' ' + self.name + ': ' 20.259 - syslog.syslog(_flags, _prefix + _strWrap(msg)) 20.260 - for i in _parseArgs(a, kwa): 20.261 - syslog.syslog(_flags, _prefix + i) 20.262 - 20.263 - def warn(self, msg, *a, **kwa): 20.264 - _flags = self.facility | syslog.LOG_WARNING 20.265 - _prefix = LOG_WARN + ' ' + self.name + ': ' 20.266 - syslog.syslog(_flags, _prefix + _strWrap(msg)) 20.267 - for i in _parseArgs(a, kwa): 20.268 - syslog.syslog(_flags, _prefix + i) 20.269 - 20.270 - def err(self, msg, *a, **kwa): 20.271 - _flags = self.facility | syslog.LOG_ERR 20.272 - _prefix = LOG_ERR + ' ' + self.name + ': ' 20.273 - syslog.syslog(_flags, _prefix + _strWrap(msg)) 20.274 - for i in _parseArgs(a, kwa): 20.275 - syslog.syslog(_flags, _prefix + i) 20.276 - 20.277 - 20.278 - 20.279 - 20.280 - 20.281 -
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 22.2 +++ b/webapp/tool/__init__.py Fri Jul 24 14:40:37 2020 +0300 22.3 @@ -0,0 +1,4 @@ 22.4 +# -*- coding: utf-8 -*- 22.5 +# --- 22.6 +# 22.7 +# --- 22.8 \ No newline at end of file
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 23.2 +++ b/webapp/tool/url.py Fri Jul 24 14:40:37 2020 +0300 23.3 @@ -0,0 +1,12 @@ 23.4 +# -*- coding: utf-8 -*- 23.5 +# --- 23.6 +# Инструменты для работы с URL 23.7 +# --- 23.8 +from base64 import urlsafe_b64encode as _b64e, urlsafe_b64decode as _b64d 23.9 +from urllib.parse import quote as urlquote 23.10 + 23.11 +def encode(buf): 23.12 + return _b64e(buf.encode('UTF-8')).decode('UTF-8') 23.13 + 23.14 +def decode(buf): 23.15 + return _b64d(buf.encode('UTF-8')).decode('UTF-8')
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 24.2 +++ b/webapp/win.py Fri Jul 24 14:40:37 2020 +0300 24.3 @@ -0,0 +1,72 @@ 24.4 +# -*- coding: utf-8 -*- 24.5 +# --- 24.6 +# Модуль среды приложений для Windows 24.7 +# --- 24.8 + 24.9 +from bottle import Bottle as App, request as Req, response as Ans, SimpleTemplate as BottleTemplate, abort as Abort 24.10 +from os.path import dirname, abspath as pAbs, split as pSplit, join as pJoin 24.11 + 24.12 +# --- CLASSES --- 24.13 +class ReqEnv: 24.14 + def __init__(self): 24.15 + self.urlBase = Req.environ.get('SCRIPT_NAME', '') 24.16 + 24.17 + def getUrl(self, url): 24.18 + url = str(url) 24.19 + buf = self.urlBase 24.20 + buf += url if url.startswith('/') else '/' + url 24.21 + return buf 24.22 + 24.23 + def __getitem__(self, key): 24.24 + return Req.environ[key] 24.25 + 24.26 + def __iter__(self): 24.27 + for key in Req.environ.keys(): 24.28 + yield key 24.29 + 24.30 + @staticmethod 24.31 + def abort(*a, **ka): 24.32 + Abort(*a, **ka) 24.33 + 24.34 + @staticmethod 24.35 + def get(self): 24.36 + return Req.GET 24.37 + 24.38 + @staticmethod 24.39 + def post(): 24.40 + return Req.POST 24.41 + 24.42 +class Env: 24.43 + def __init__(self, scFile): 24.44 + self.wd = pSplit(dirname(pAbs(scFile)))[0] 24.45 + self.static = pJoin(self.wd, 'static') 24.46 + 24.47 + def http(self, tplName): 24.48 + return BottleTemplate(name=pJoin(self.wd, 'http', '%s.html' % tplName)).render 24.49 + 24.50 + def db(self): 24.51 + """ 24.52 + Путь к базе SQLite, иные типы БД дожны иметь более 24.53 + серьёзные средства авторизации 24.54 + """ 24.55 + return pJoin(self.wd, 'db.sqlite') 24.56 + 24.57 + @staticmethod 24.58 + def httpTpl(buf): 24.59 + return BottleTemplate(buf).render 24.60 + 24.61 + @staticmethod 24.62 + def app(): 24.63 + return App() 24.64 + 24.65 + @staticmethod 24.66 + def appRun(app): 24.67 + app.run(server='cgi') 24.68 + 24.69 +# --- PROCS --- 24.70 +def runApp(app): 24.71 + Env.appRun(app) 24.72 + 24.73 +def Template(buf): 24.74 + return Env.httpTpl(buf) 24.75 + 24.76 \ No newline at end of file