141 lines
4.2 KiB
Python
141 lines
4.2 KiB
Python
import functools
|
|
import ldap
|
|
import re
|
|
import flask
|
|
|
|
import webapp
|
|
from webapp import app, context, config, ldaputils
|
|
|
|
bp = flask.Blueprint('admin', __name__)
|
|
|
|
def admin_required_impl(f):
|
|
@functools.wraps(f)
|
|
def func(*a, **kw):
|
|
# TODO: Actually check for admin perms
|
|
if not flask.session['is_admin']:
|
|
flask.abort(403)
|
|
|
|
return f(*a, **kw)
|
|
return func
|
|
|
|
def admin_required(f):
|
|
return webapp.auth.login_required(admin_required_impl(f))
|
|
|
|
def _get_user_list(conn, query='&'):
|
|
"""Returns List[Tuple[username, full name]] for query"""
|
|
all_users = []
|
|
|
|
results = conn.search_s(config.ldap_people, ldap.SCOPE_SUBTREE, f'(&(uid=*)(cn=*){ldaputils.wrap(query)})', attrlist=['uid', 'cn'])
|
|
for user, attrs in results:
|
|
user_uid = attrs['uid'][0].decode()
|
|
user_cn = attrs['cn'][0].decode()
|
|
all_users.append((user_uid, user_cn))
|
|
|
|
all_users.sort(key=lambda user: user[0].lower())
|
|
return all_users
|
|
|
|
def _get_groupped_user_list(conn):
|
|
"""Returns all users (uid, full name), groupped by active groups"""
|
|
groupped_users = [
|
|
(group.capitalize(), _get_user_list(conn, f'memberOf={ldaputils.group_dn(group)}'))
|
|
for group in config.ldap_active_groups
|
|
]
|
|
|
|
inactive_filter = ldaputils._not(
|
|
ldaputils.member_of_any(config.ldap_active_groups)
|
|
)
|
|
|
|
groupped_users.append(
|
|
('Inactive users', _get_user_list(conn, inactive_filter))
|
|
)
|
|
|
|
return groupped_users
|
|
|
|
@bp.route('/admin/')
|
|
@admin_required
|
|
def admin_view():
|
|
return flask.render_template('admin/index.html')
|
|
|
|
@bp.route('/admin/users/')
|
|
@admin_required
|
|
def admin_users_view():
|
|
conn = context.get_connection()
|
|
groups = _get_groupped_user_list(conn)
|
|
|
|
return flask.render_template('admin/users.html', groups=groups)
|
|
|
|
def _get_profile(conn, uid):
|
|
results = conn.search_s(ldaputils.user_dn(uid), ldap.SCOPE_SUBTREE)
|
|
return ldaputils.normalized_entries(results)[0]
|
|
|
|
def _format_profile(profile):
|
|
rendered = []
|
|
dn, attrs = profile
|
|
for attr, value in attrs:
|
|
attr_sanitized = attr.lower()
|
|
attr_full_name = config.full_name.get(attr_sanitized, attr_sanitized)
|
|
attr_readable_name = config.readable_names.get(attr_full_name)
|
|
rendered.append((attr, attr_readable_name, value))
|
|
|
|
# Attributes with readable names first, then by name
|
|
rendered.sort(key=lambda profile: profile[0])
|
|
rendered.sort(key=lambda profile: profile[1] is None)
|
|
return rendered
|
|
|
|
def _get_groups_of(conn, uid):
|
|
filter = ldaputils.groups_of_user(uid)
|
|
groups = [
|
|
attrs['cn'][0].decode()
|
|
for group_dn, attrs in
|
|
conn.search_s(config.ldap_base, ldap.SCOPE_SUBTREE, filter)
|
|
]
|
|
|
|
return groups
|
|
|
|
@bp.route('/admin/users/<uid>')
|
|
@admin_required
|
|
def admin_user_view(uid):
|
|
ldaputils.validate_name(uid)
|
|
conn = context.get_connection()
|
|
|
|
profile = _get_profile(conn, uid)
|
|
groups = _get_groups_of(conn, uid)
|
|
|
|
return flask.render_template('admin/user.html', uid=uid, profile=_format_profile(profile), groups=groups)
|
|
|
|
@bp.route('/admin/groups/')
|
|
@admin_required
|
|
def admin_groups_view():
|
|
conn = context.get_connection()
|
|
|
|
# no obvious way to filter out groups that are just a per-user-group
|
|
# (not super useful to look at them)
|
|
# so we'll filter them out by name yolo
|
|
all_users = _get_user_list(conn)
|
|
all_uids = set([uid for uid, cn in all_users])
|
|
|
|
groups = [
|
|
attrs['cn'][0].decode()
|
|
for group_dn, attrs in
|
|
conn.search_s(config.ldap_base, ldap.SCOPE_SUBTREE, 'objectClass=groupOfUniqueNames')
|
|
]
|
|
|
|
filter_groups = filter((lambda cn: cn not in all_uids), groups)
|
|
|
|
return flask.render_template('admin/groups.html', groups=filter_groups)
|
|
|
|
def _get_group(conn, name):
|
|
results = conn.search_s(ldaputils.group_dn(name), ldap.SCOPE_SUBTREE)
|
|
return ldaputils.normalized_entries(results)[0]
|
|
|
|
@bp.route('/admin/groups/<name>')
|
|
@admin_required
|
|
def admin_group_view(name):
|
|
ldaputils.validate_name(name)
|
|
conn = context.get_connection()
|
|
|
|
group_attrs = _get_group(conn, name)
|
|
members = _get_user_list(conn, f'memberOf={ldaputils.group_dn(name)}')
|
|
|
|
return flask.render_template('admin/group.html', name=name, attributes=_format_profile(group_attrs), members=members)
|