pyceo/ceod/api/groups.py

112 lines
3.7 KiB
Python

from flask import Blueprint, request
from flask.json import jsonify
from zope import component
from .utils import authz_restrict_to_syscom, is_truthy, \
create_streaming_response, development_only, requires_admin_creds, \
requires_authentication_no_realm, user_is_in_group
from ceo_common.interfaces import ILDAPService
from ceo_common.utils import fuzzy_result, fuzzy_match
from ceod.transactions.groups import (
AddGroupTransaction,
AddMemberToGroupTransaction,
RemoveMemberFromGroupTransaction,
DeleteGroupTransaction,
)
from heapq import heappushpop, nlargest
bp = Blueprint('groups', __name__)
@bp.route('/', methods=['POST'], strict_slashes=False)
@authz_restrict_to_syscom
def create_group():
body = request.get_json(force=True)
txn = AddGroupTransaction(
cn=body['cn'],
description=body['description'],
)
return create_streaming_response(txn)
@bp.route('/<group_name>')
def get_group(group_name):
ldap_srv = component.getUtility(ILDAPService)
group = ldap_srv.get_group(group_name)
return group.to_dict()
@bp.route('/search/<query>/<count>')
def search_group(query, count):
query = str(query)
count = int(count)
ldap_srv = component.getUtility(ILDAPService)
clubs = ldap_srv.get_clubs()
scores = [fuzzy_result("", 99999) for _ in range(count)]
for club in clubs:
score = fuzzy_match(query, str(club.cn))
result = fuzzy_result(str(club.cn), score)
heappushpop(scores, result)
result = [score.string for score in nlargest(count, scores)]
return jsonify(result)
def may_add_user_to_group(auth_username: str, group_name: str) -> bool:
# (is syscom) OR (group is office AND client is offsck)
if user_is_in_group(auth_username, 'syscom'):
return True
if group_name == 'office':
ldap_srv = component.getUtility(ILDAPService)
auth_user = ldap_srv.get_user(auth_username)
if 'offsck' in auth_user.positions:
return True
return False
@bp.route('/<group_name>/members/<username>', methods=['POST'])
@requires_admin_creds
@requires_authentication_no_realm
def add_member_to_group(auth_username: str, group_name: str, username: str):
# Admin creds are required because slapd does not support access control
# rules which use the client's attributes
if not may_add_user_to_group(auth_username, group_name):
return {'error': "not authorized to add user to group"}, 403
subscribe_to_lists = is_truthy(
request.args.get('subscribe_to_lists', 'true')
)
txn = AddMemberToGroupTransaction(
username=username,
group_name=group_name,
subscribe_to_lists=subscribe_to_lists,
)
return create_streaming_response(txn)
@bp.route('/<group_name>/members/<username>', methods=['DELETE'])
@requires_admin_creds
@requires_authentication_no_realm
def remove_member_from_group(auth_username: str, group_name: str, username: str):
# Admin creds are required because slapd does not support access control
# rules which use the client's attributes
if not may_add_user_to_group(auth_username, group_name):
return {'error': "not authorized to add user to group"}, 403
unsubscribe_from_lists = is_truthy(
request.args.get('unsubscribe_from_lists', 'true')
)
txn = RemoveMemberFromGroupTransaction(
username=username,
group_name=group_name,
unsubscribe_from_lists=unsubscribe_from_lists,
)
return create_streaming_response(txn)
@bp.route('/<group_name>', methods=['DELETE'])
@authz_restrict_to_syscom
@development_only
def delete_group(group_name):
txn = DeleteGroupTransaction(group_name)
return create_streaming_response(txn)