*: port to python3, update deps, fix cn mod

pull/1/head
q3k 2019-10-18 14:13:10 +02:00
parent 934e4ed67d
commit 50dbd3087a
7 changed files with 53 additions and 38 deletions

View File

@ -7,7 +7,7 @@ RUN set -e -x ;\
export DEBIAN_FRONTEND=noninteractive ;\
apt-get -y update ;\
apt-get -y upgrade ;\
apt-get -y install python-dev python-virtualenv build-essential libkrb5-dev libsasl2-dev libldap2-dev libssl-dev krb5-user ;\
apt-get -y install python3-dev python3-virtualenv build-essential libkrb5-dev libsasl2-dev libldap2-dev libssl-dev krb5-user ;\
rm -rf /var/lib/apt/lists
COPY krb5.conf /etc/krb5.conf
@ -21,7 +21,7 @@ USER app
COPY requirements.txt /app/requirements.txt
RUN set -e -x ;\
virtualenv /app/venv ;\
python3 -m venv /app/venv ;\
/app/venv/bin/pip install -r /app/requirements.txt
COPY --chown=app runserver.py /app/

View File

@ -1,13 +1,15 @@
Babel==2.3.4
click==6.6
Flask==0.11.1
Flask-Babel==0.11.1
Flask-WTF==0.13.1
itsdangerous==0.24
Jinja2==2.8
MarkupSafe==0.23
Babel==2.7.0
Click==7.0
Flask==1.1.1
Flask-Babel==0.12.2
Flask-WTF==0.14.2
itsdangerous==1.1.0
Jinja2==2.10.3
kerberos==1.3.0
python-ldap==2.4.28
pytz==2016.10
Werkzeug==0.11.11
WTForms==2.1
MarkupSafe==1.1.1
pyasn1==0.4.7
pyasn1-modules==0.2.7
python-ldap==3.2.0
pytz==2019.3
Werkzeug==0.16.0
WTForms==2.2.1

View File

@ -1,8 +1,9 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import flask
import flask_wtf
import wtforms
from functools import reduce
from flask_babel import gettext
@ -35,11 +36,10 @@ def initialize_forms():
forms = {}
for f in reduce(lambda a,b: a | b, config.can.values()):
cls, attrs = config.fields.get(f, config.default_field)
class AddForm(flask_wtf.Form):
class AddForm(flask_wtf.FlaskForm):
value = cls(label=config.readable_names.get(f), **attrs)
AddForm.__name__ == 'Add' + f
forms[f] = AddForm
print f, forms[f]
return forms
@app.before_first_request

View File

@ -13,8 +13,8 @@ class Attr(object):
self.name = name
self.readable_name = config.readable_names.get(name, name)
self.value = value
self.uid = hashlib.sha1(name + value).hexdigest()
def __unicode__(self):
self.uid = hashlib.sha1(name.encode('utf-8') + value).hexdigest()
def __str__(self):
return self.value.decode('utf-8')
def get_dn():
@ -41,7 +41,7 @@ def refresh_profile(dn=None):
res = conn.search_s(dn, ldap.SCOPE_SUBTREE)
assert(len(res) == 1)
profile = {}
for attr, vs in res[0][1].iteritems():
for attr, vs in res[0][1].items():
for v in vs:
a = Attr(attr, v)
profile[a.uid] = a

View File

@ -35,10 +35,14 @@ class LRUPool(threading.Thread):
time.sleep(self.timeout / 2)
self.lock.acquire()
now = time.time()
drop = set()
for k, [c, atime] in self.pool.items():
if now - atime > self.timeout:
log.info("Pool {} dropping {}.", self, k)
self._drop(k)
drop.add(k)
for k in list(drop):
self._drop(k)
self.lock.release()
def register_callback(self, action, cb):
self.callbacks.setdefault(action, []).append(cb)

View File

@ -18,10 +18,10 @@ def validate(predicate, arg, error='Error!', redirect='/'):
return decorator
def sanitize_perms():
config.can = { k: set(map(sanitize_ldap, v)) for k,v in config.can.iteritems() }
config.can = { k: set(map(sanitize_ldap, v)) for k,v in config.can.items() }
def sanitize_readable():
config.readable_names = { sanitize_ldap(k): v for k, v in config.readable_names.iteritems() }
config.readable_names = { sanitize_ldap(k): v for k, v in config.readable_names.items() }
def sanitize_ldap(k):
k = k.lower()

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import logging
import urllib
@ -26,7 +26,7 @@ def login_required(f):
if not conn:
flask.session.clear()
flask.flash('You must log in to continue', category='warning')
return flask.redirect('/login?' + urllib.urlencode({'goto': flask.request.path}))
return flask.redirect('/login?' + urllib.parse.urlencode({'goto': flask.request.path}))
return f(*a, **kw)
return func
@ -40,7 +40,7 @@ def api_access(f):
return func
def req_to_ctx():
return dict(flask.request.form.iteritems())
return dict(flask.request.form.items())
def str_to_ldap(s):
return s.encode('utf-8')
@ -52,7 +52,7 @@ def attr_op(op, attrName, uid = None, templates=config.std_templates, success_re
if uid:
attr = context.get_profile()[uid]
attrName = attr.name
old_value = attr.value
old_value = str(attr)
form = DelForm() if op == 'del' else app.forms[attrName](value=old_value)
form.attr_data = attr
if attrName not in config.can[op]:
@ -64,16 +64,25 @@ def attr_op(op, attrName, uid = None, templates=config.std_templates, success_re
admin = attrName in config.admin_required
conn = context.get_admin_connection() if admin else context.get_connection()
dn = context.get_dn()
if op in ['del', 'mod']:
# Most fields should be modified by remove/add.
if op == 'mod' and attrName not in ['commonname']:
op = 'modreadd'
if op in ['del', 'modreadd']:
conn.modify_s(dn, [(ldap.MOD_DELETE, attrName, str_to_ldap(old_value))])
if op in ['add', 'mod']:
if op in ['add', 'modreadd']:
conn.modify_s(dn, [(ldap.MOD_ADD, attrName, str_to_ldap(new_value))])
if op in ['mod']:
conn.modify_s(dn, [(ldap.MOD_REPLACE, attrName, str_to_ldap(new_value))])
context.refresh_profile()
return flask.redirect(success_redirect)
if form.is_submitted():
for field, errors in form.errors.items():
for error in errors:
flask.flash(u"Error in the %s field - %s" % (
flask.flash(u"Error in the {} field - {}".format(
getattr(form, field).label.text,
error
), 'danger')
@ -82,18 +91,18 @@ def attr_op(op, attrName, uid = None, templates=config.std_templates, success_re
return flask.make_response(flask.render_template(templates[op], fatal_redirect=fatal_redirect,
attr_op=op, form=form, attr_name=attrName, uid=uid), status)
except ldap.LDAPError as e:
print e
print('LDAP error:', e)
flask.flash('Could not modify profile', 'danger')
return flask.redirect(fatal_redirect)
def connect_to_ldap(dn, password):
try:
return app.connections.bind(dn, password)
except ldap.LDAPError, error_message:
print "Could not connect to server: %s" % error_message
except ldap.LDAPError as error_message:
print("Could not connect to server:", error_message)
return None
class DelForm(flask_wtf.Form):
class DelForm(flask_wtf.FlaskForm):
pass
@app.route('/login', methods=["GET"])
@ -135,7 +144,7 @@ def _passwd_ldap(current, new):
conn.passwd_s(dn, current. new)
return True
except ldap.LDAPError as e:
print e
print('LDAP error:', e)
return False
def _passwd_kadmin(current, new):
@ -144,7 +153,7 @@ def _passwd_kadmin(current, new):
principal_name = config.kadmin_principal_map.format(username)
return kerberos.changePassword(principal_name, current, new)
except Exception as e:
print e
print('Kerberos error:', e)
logging.exception('kpasswd failed')
return False
@ -211,13 +220,13 @@ def claim_nick():
try:
conn.modify_s(owner, [(ldap.MOD_ADD, config.irc_attr, str_to_ldap(nick))])
app.tokens.drop(owner)
print 'Token claimed for %s (nick %s, token %s)' % (owner, nick, token)
print('Token claimed for {} (nick {}, token {})'.format(owner, nick, token))
status = 200
error = 'Success'
context.refresh_profile(owner)
except ldap.LDAPError as e:
error = 'Could not claim irc nick, contact an admin'
print e
print('LDAP Error:', e)
res = flask.make_response(error, status)
res.mimetype = 'text/plain'
return res