ldapweb/webapp/admin.py

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)