positions api

This commit is contained in:
Rio Liu 2021-08-17 00:43:33 -04:00 committed by Rio6
parent 8dcada4090
commit 4e578d0401
8 changed files with 91 additions and 1 deletions

View File

@ -7,6 +7,11 @@ First, make sure that you have installed the
This will setup all of the services needed for ceo to work. You should clone
this repo in one of the dev environment containers.
Install required system package for pyceo:
```sh
apt install libkrb5-dev libsasl2-dev libldap2-dev
```
Next, install and activate a virtualenv:
```sh
sudo apt install libkrb5-dev python3-dev

View File

@ -18,6 +18,9 @@ class ILDAPService(Interface):
def get_user(username: str) -> IUser:
"""Retrieve the user with the given username."""
def get_users_with_positions(self) -> List[IUser]:
"""Retrieve users who has their position set"""
def add_user(user: IUser):
"""
Add the user to the database.

View File

@ -31,8 +31,9 @@ def create_app(flask_config={}):
# Only ceod_admin_host should serve the /api/members endpoints because
# it needs to run kadmin
if hostname == cfg.get('ceod_admin_host'):
from ceod.api import members
from ceod.api import members, positions
app.register_blueprint(members.bp, url_prefix='/api/members')
app.register_blueprint(positions.bp, url_prefix='/api/positions')
if hostname == cfg.get('ceod_mailman_host'):
# Only offer mailman API if this host is running Mailman

41
ceod/api/positions.py Normal file
View File

@ -0,0 +1,41 @@
import json
from flask import Blueprint, request
from zope import component
from ceod.transactions.members import UpdateMemberPositionsTransaction
from .utils import authz_restrict_to_syscom, requires_authentication_no_realm, create_streaming_response
from ceo_common.interfaces import ILDAPService
bp = Blueprint('positions', __name__)
@bp.route('/', methods=['GET'])
@requires_authentication_no_realm
def get_positions(auth_user: str):
ldap_srv = component.getUtility(ILDAPService)
users = ldap_srv.get_users_with_positions()
positions = {}
for user in users:
for position in user.positions:
positions[position] = user.uid
return json.dumps(positions)
@bp.route('/', methods=['POST'])
@authz_restrict_to_syscom
def update_positions():
body = request.get_json(force=True)
# TODO verify json
# Reverse the dict so it's easier to use
member_positions = {}
for position, user in body.items():
if user not in member_positions:
member_positions[user] = []
member_positions[user].append(position)
txn = UpdateMemberPositionsTransaction(member_positions)
return create_streaming_response(txn)
# TODO un/subscribe to mailing list

View File

@ -82,6 +82,11 @@ class LDAPService:
entry = self._get_readable_entry_for_group(conn, cn)
return Group.deserialize_from_ldap(entry)
def get_users_with_positions(self) -> List[IUser]:
conn = self._get_ldap_conn(False)
conn.search(self.ldap_users_base, '(position=*)', attributes=ldap3.ALL_ATTRIBUTES)
return [User.deserialize_from_ldap(entry) for entry in conn.entries]
def uid_to_dn(self, uid: str):
return f'uid={uid},{self.ldap_users_base}'

View File

@ -168,6 +168,11 @@ class User:
entry.position.delete(position)
self.positions.remove(position)
def set_positions(self, positions: List[str]):
with self.ldap_srv.entry_ctx_for_user(self) as entry:
entry.position = positions
self.positions = positions
def get_forwarding_addresses(self) -> List[str]:
return self.file_srv.get_forwarding_addresses(self)

View File

@ -0,0 +1,29 @@
from typing import List, Dict
from zope import component
from ..AbstractTransaction import AbstractTransaction
from ceo_common.interfaces import ILDAPService
class UpdateMemberPositionsTransaction(AbstractTransaction):
"""
Transaction to update member's positions, and remove positions for anyone that's not in the list
"""
# Positions is a dict where keys are member names and values are the list of positions they have
def __init__(self, positions: Dict[str, List[str]]):
super().__init__()
self.positions = positions
self.ldap_srv = component.getUtility(ILDAPService)
self.old_positions = {}
def child_execute_iter(self):
for username, positions in self.positions.items():
user = self.ldap_srv.get_user(username)
user.set_positions(positions)
yield f'update_positions_{username}'
self.finish(None)
def rollback(self):
# TODO
pass

View File

@ -2,3 +2,4 @@ from .AddMemberTransaction import AddMemberTransaction
from .ModifyMemberTransaction import ModifyMemberTransaction
from .RenewMemberTransaction import RenewMemberTransaction
from .DeleteMemberTransaction import DeleteMemberTransaction
from .UpdateMemberPositionsTransaction import UpdateMemberPositionsTransaction