diff --git a/db.py b/db.py index c0c13e05e32561017d5bdb96b284edfc32d9b604..056c41b0e5c1244440f63803b03196a20afa784a 100644 --- a/db.py +++ b/db.py @@ -1,74 +1,82 @@ from server import * -import sqlite3 import re if config['DB_ENGINE'] == 'sqlite': - created = not os.path.exists(config['SQLITE_DB']) - db = sqlite3.connect(config['SQLITE_DB']) - cur = db.cursor() - if config['SQLITE_INIT_SCHEMA']: - cur.executescript(open(config['DB_SCHEMA']).read()) - if config['SQLITE_INIT_DATA'] and created: - cur.executescript(open(config['DB_DATA']).read()) - db.commit() - db.close() - -# Row wrapper for sqlite -def dict_factory(cursor, row): - d = {} - for idx, col in enumerate(cursor.description): - if type(row[idx]) == str: - d[col[0].split('.')[-1]] = row[idx].replace('\\n','\n').replace('\\r','\r') - else: - d[col[0].split('.')[-1]] = row[idx] - return d - -# From sqlite3 module, but with error catching -def convert_timestamp(val): - try: - datepart, timepart = val.split(b" ") - year, month, day = map(int, datepart.split(b"-")) - timepart_full = timepart.split(b".") - hours, minutes, seconds = map(int, timepart_full[0].split(b":")) - val = datetime(year, month, day, hours, minutes, seconds, 0) - except ValueError: - val = None - return val - -sqlite3.register_converter('datetime', convert_timestamp) -sqlite3.register_converter('timestamp', convert_timestamp) - -def query(operation, *params): - _params = [] - for p in params: - if isinstance(p, datetime): - p = p.replace(microsecond=0) - _params.append(p) - params = _params - if config['DB_ENGINE'] == 'mysql': - import mysql.connector - if 'db' not in g or not g.db.is_connected(): - g.db = mysql.connector.connect(user=config['MYSQL_USER'], password=config['MYSQL_PASSWD'], host=config['MYSQL_HOST'], database=config['MYSQL_DB']) - if not hasattr(request, 'db'): - request.db = g.db.cursor(dictionary=True) - request.db.execute(operation.replace('?', '%s'), params) - elif config['DB_ENGINE'] == 'sqlite': + import sqlite3 + + # From sqlite3 module, but with error catching + def convert_timestamp(val): + try: + datepart, timepart = val.split(b" ") + year, month, day = map(int, datepart.split(b"-")) + timepart_full = timepart.split(b".") + hours, minutes, seconds = map(int, timepart_full[0].split(b":")) + val = datetime(year, month, day, hours, minutes, seconds, 0) + except ValueError: + val = None + return val + + sqlite3.register_converter('datetime', convert_timestamp) + sqlite3.register_converter('timestamp', convert_timestamp) + + if config['DB_ENGINE'] == 'sqlite': + created = not os.path.exists(config['SQLITE_DB']) + db = sqlite3.connect(config['SQLITE_DB']) + cur = db.cursor() + if config['SQLITE_INIT_SCHEMA']: + cur.executescript(open(config['DB_SCHEMA']).read()) + if config['SQLITE_INIT_DATA'] and created: + cur.executescript(open(config['db_data']).read()) + db.commit() + db.close() + + def get_dbcursor(): if 'db' not in g: g.db = sqlite3.connect(config['SQLITE_DB'], detect_types=sqlite3.PARSE_DECLTYPES) - g.db.row_factory = dict_factory g.db.isolation_level = None if not hasattr(request, 'db'): request.db = g.db.cursor() - request.db.execute(operation, params) - else: - return [] - try: - rows = request.db.fetchall() - except: - rows = [] - if not rows and request.db.lastrowid != None: - return request.db.lastrowid - return rows + return request.db + + def fix_query(operation, params): + params = [(p.replace(microsecond=0) if isinstance(p, datetime) else p) for p in params] + return operation, params + +elif config['DB_ENGINE'] == 'mysql': + import mysql.connector + + def get_dbcursor(): + if 'db' not in g or not g.db.is_connected(): + g.db = mysql.connector.connect(user=config['MYSQL_USER'], password=config['MYSQL_PASSWD'], host=config['MYSQL_HOST'], database=config['MYSQL_DB']) + if not hasattr(request, 'db'): + request.db = g.db.cursor() + return request.db + + def fix_query(operation, params): + operation = operation.replace('?', '%s') + params = [(p.replace(microsecond=0) if isinstance(p, datetime) else p) for p in params] + return operation, params + +def query(operation, *params, delim="sep"): + operation, params = fix_query(operation, params) + cur = get_dbcursor() + cur.execute(operation, params) + rows = cur.fetchall() + res = [] + for row in rows: + res.append({}) + for col, desc in zip(row, cur.description): + name = desc[0].split('.')[-1] + if type(col) == str: + col = col.replace('\\n', '\n').replace('\\r', '\r') + res[-1][name] = col + return res + +def modify(operation, *params): + operation, params = fix_query(operation, params) + cur = get_dbcursor() + cur.execute(operation, params) + return cur.lastrowid @app.teardown_request def commit_db(*args): @@ -94,15 +102,11 @@ def searchquery(text, columns, match, tables, suffix, *suffixparams): return query(expr, *params, *suffixparams) LDAP_USERRE = re.compile(r'[^a-z0-9]') -notldap = { - 'videoag':('videoag', ['users','videoag'], {'uid': 'videoag', 'givenName': 'Video', 'sn': 'Geier'}), - 'gustav':('passwort', ['users'], {'uid': 'gustav', 'givenName': 'Gustav', 'sn': 'Geier'}) -} - -def ldapauth(user, password): - user = LDAP_USERRE.sub(r'', user.lower()) - if 'LDAP_HOST' in config: - import ldap3 +if 'LDAP_HOST' in config: + import ldap3 + + def ldapauth(user, password): + user = LDAP_USERRE.sub(r'', user.lower()) try: conn = ldap3.Connection(config['LDAP_HOST'], 'uid=%s,ou=users,dc=fsmpi,dc=rwth-aachen,dc=de'%user, password, auto_bind=True) if conn.search("ou=groups,dc=fsmpi,dc=rwth-aachen,dc=de", "(&(cn=*)(memberUid=%s))"%user, attributes=['cn']): @@ -111,14 +115,9 @@ def ldapauth(user, password): return user, groups except ldap3.core.exceptions.LDAPBindError: pass - elif config.get('DEBUG') and user in notldap and password == notldap[user][0]: - return user, notldap[user][1] - return None, [] - -def ldapget(user): - user = LDAP_USERRE.sub(r'', user.lower()) - if 'LDAP_HOST' in config: - import ldap3 + + def ldapget(user): + user = LDAP_USERRE.sub(r'', user.lower()) conn = ldap3.Connection('ldaps://rumo.fsmpi.rwth-aachen.de', auto_bind=True) conn.search("ou=users,dc=fsmpi,dc=rwth-aachen,dc=de", "(uid=%s)"%user, attributes=ldap3.ALL_ATTRIBUTES) @@ -126,6 +125,19 @@ def ldapget(user): return {} e = conn.entries[0] return {'uid': user, 'givenName': e.givenName.value, 'sn':e.sn.value} - else: - return notldap[user][2] +else: + notldap = { + 'videoag':('videoag', ['users','videoag'], {'uid': 'videoag', 'givenName': 'Video', 'sn': 'Geier'}), + 'gustav':('passwort', ['users'], {'uid': 'gustav', 'givenName': 'Gustav', 'sn': 'Geier'}) + } + + def ldapauth(user, password): + user = LDAP_USERRE.sub(r'', user.lower()) + if config.get('DEBUG') and user in notldap and password == notldap[user][0]: + return user, notldap[user][1] + return None, [] + + def ldapget(user): + user = LDAP_USERRE.sub(r'', user.lower()) + return notldap[user][2] diff --git a/importer.py b/importer.py index f98381fde70aceb70f33a25956cfb79ce3231158..b47f0f94766a327bf84981b5873129e4b065fc73 100755 --- a/importer.py +++ b/importer.py @@ -17,7 +17,7 @@ def import_from(source=None, id=None): for i in campus: if i.startswith('new'): if campus[i]['url'] != '': - query('INSERT INTO import_campus (url, type, course_id, last_checked, changed) VALUES (?, ?, ?, ?, 1)',campus[i]['url'],campus[i]['type'],id,datetime.now()) + modify('INSERT INTO import_campus (url, type, course_id, last_checked, changed) VALUES (?, ?, ?, ?, 1)',campus[i]['url'],campus[i]['type'],id,datetime.now()) else: if campus[i]['url'] != '': query('UPDATE import_campus SET url = ?, `type` = ? WHERE (course_id = ?) AND (id = ?)', campus[i]['url'],campus[i]['type'],id,int(i)) diff --git a/server.py b/server.py index 1c2ab2de4d429178dd08fb0dcbb650ba8dca1679..15812dbfa3891a9f6567bad5c4b77096f8e33a53 100755 --- a/server.py +++ b/server.py @@ -34,7 +34,7 @@ if config['DEBUG']: if not config.get('SECRET_KEY', None): config['SECRET_KEY'] = os.urandom(24) -from db import query, searchquery, ldapauth, ldapget, convert_timestamp +from db import query, modify, searchquery, ldapauth, ldapget mod_endpoints = [] @@ -235,7 +235,7 @@ def login(): session['user'] = ldapget(user) dbuser = query('SELECT * FROM users WHERE name = ?', user) if not dbuser: - query('INSERT INTO users (name, realname, fsacc, level, calendar_key, rfc6238) VALUES (?, ?, ?, 1, "", "")', user, session['user']['givenName'], user) + modify('INSERT INTO users (name, realname, fsacc, level, calendar_key, rfc6238) VALUES (?, ?, ?, 1, "", "")', user, session['user']['givenName'], user) dbuser = query('SELECT * FROM users WHERE name = ?', user) session['user']['dbid'] = dbuser[0]['id'] return redirect(request.values.get('ref', url_for('index'))) @@ -259,7 +259,7 @@ def edit(prefix="", ignore=[]): 'chapters': ('chapters', 'id', ['time', 'text', 'visible', 'deleted']), 'announcements': ('announcements', 'id', ['text', 'internal', 'level', 'visible', 'deleted']) } - query('BEGIN') + modify('BEGIN') if request.is_json: changes = request.get_json().items() else: @@ -267,21 +267,19 @@ def edit(prefix="", ignore=[]): for key, val in changes: if key in ignore: continue - print('edit:', key, val) key = prefix+key - print (key,val) table, id, column = key.split('.', 2) assert table in tabs assert column in tabs[table][2] - query('INSERT INTO changelog ("table",id_value,id_key,field,value_new,value_old,"when",who,executed) VALUES (?,?,?,?,?,(SELECT %s FROM %s WHERE %s = ?),?,?,1)'%(column,tabs[table][0],tabs[table][1]),table,id,tabs[table][1],column,val,id,datetime.now(),session['user']['givenName']) - query('UPDATE %s SET %s = ?, time_updated = ? WHERE %s = ?'%(tabs[table][0], column, tabs[table][1]), val, datetime.now(), id) - query('COMMIT') + modify('INSERT INTO changelog ("table",id_value,id_key,field,value_new,value_old,"when",who,executed) VALUES (?,?,?,?,?,(SELECT %s FROM %s WHERE %s = ?),?,?,1)'%(column,tabs[table][0],tabs[table][1]),table,id,tabs[table][1],column,val,id,datetime.now(),session['user']['givenName']) + modify('UPDATE %s SET %s = ?, time_updated = ? WHERE %s = ?'%(tabs[table][0], column, tabs[table][1]), val, datetime.now(), id) + modify('COMMIT') return "OK", 200 @app.route('/newcourse', methods=['GET', 'POST']) @mod_required def new_course(): - id = query(''' + id = modify(''' INSERT INTO courses_data (visible, title, short, handle, organizer, subject, created_by, time_created, time_updated, semester, settings, description, internal, responsible, feed_url) @@ -296,7 +294,7 @@ def new_course(): @app.route('/newlecture/<courseid>', methods=['GET', 'POST']) @mod_required def new_lecture(courseid): - id = query(''' + id = modify(''' INSERT INTO lectures_data (course_id, visible, drehplan, title, comment, internal, speaker, place, time, time_created, time_updated, jumplist, titlefile) @@ -344,7 +342,7 @@ def auth(): # For use with nginx auth_request if not types[0] or allowed or ismod() or \ (auth and check_mod(*ldapauth(auth.username, auth.password))): return 'OK', 200 - query('INSERT INTO log VALUES (?, "", ?, "video", ?, ?)', ip, datetime.now(), videos[0]['id'], url) + modify('INSERT INTO log VALUES (?, "", ?, "video", ?, ?)', ip, datetime.now(), videos[0]['id'], url) elif 'password' in types: return Response("Login required", 401, {'WWW-Authenticate': 'Basic realm="Login Required"'}) return "Not allowed", 403 @@ -375,7 +373,7 @@ def suggest_chapter(lectureid): submitter = None if not ismod(): submitter = request.environ['REMOTE_ADDR'] - id = query('INSERT INTO chapters (lecture_id, time, text, time_created, time_updated, created_by, submitted_by) VALUES (?, ?, ?, ?, ?, ?, ?)', + id = modify('INSERT INTO chapters (lecture_id, time, text, time_created, time_updated, created_by, submitted_by) VALUES (?, ?, ?, ?, ?, ?, ?)', lectureid, time, text, datetime.now(), datetime.now(), session.get('user', {'dbid':None})['dbid'], submitter) if 'ref' in request.values: return redirect(request.values['ref']) @@ -384,7 +382,7 @@ def suggest_chapter(lectureid): @app.route('/newpsa', methods=['POST', 'GET']) @mod_required def new_announcement(): - id = query('INSERT INTO announcements (text, internal, time_created, time_updated, created_by) VALUES ("Neue Ankündigung", "", ?, ?, ?)', + id = modify('INSERT INTO announcements (text, internal, time_created, time_updated, created_by) VALUES ("Neue Ankündigung", "", ?, ?, ?)', datetime.now(), datetime.now(), session.get('user', {'dbid':None})['dbid']) if 'ref' in request.values: return redirect(request.values['ref']) @@ -393,7 +391,7 @@ def new_announcement(): @app.route('/newfeatured', methods=['POST', 'GET']) @mod_required def new_featured(): - id = query('INSERT INTO featured (time_created, time_updated, created_by) VALUES (?, ?, ?)', + id = modify('INSERT INTO featured (time_created, time_updated, created_by) VALUES (?, ?, ?)', datetime.now(), datetime.now(), session.get('user', {'dbid':None})['dbid']) if 'ref' in request.values: return redirect(request.values['ref'])