diff --git a/ceo/cli/entrypoint.py b/ceo/cli/entrypoint.py index 04f5306..2248938 100644 --- a/ceo/cli/entrypoint.py +++ b/ceo/cli/entrypoint.py @@ -8,6 +8,7 @@ from zope import component from ..krb_check import krb_check from .members import members from .groups import groups +from .positions import positions from .updateprograms import updateprograms from ceo_common.interfaces import IConfig, IHTTPClient from ceo_common.model import Config, HTTPClient @@ -29,10 +30,14 @@ def cli(ctx): cli.add_command(members) cli.add_command(groups) +cli.add_command(positions) cli.add_command(updateprograms) def register_services(): + # Using base component directly so events get triggered + baseComponent = component.getGlobalSiteManager() + # Config # This is a hack to determine if we're in the dev env or not if socket.getfqdn().endswith('.csclub.internal'): @@ -41,8 +46,8 @@ def register_services(): else: config_file = os.environ.get('CEO_CONFIG', '/etc/csc/ceo.ini') cfg = Config(config_file) - component.provideUtility(cfg, IConfig) + baseComponent.registerUtility(cfg, IConfig) # HTTPService http_client = HTTPClient() - component.provideUtility(http_client, IHTTPClient) + baseComponent.registerUtility(http_client, IHTTPClient) diff --git a/ceo/cli/positions.py b/ceo/cli/positions.py new file mode 100644 index 0000000..875d1f7 --- /dev/null +++ b/ceo/cli/positions.py @@ -0,0 +1,48 @@ +import click +from zope import component +from zope.interface.interfaces import IRegistered, IUtilityRegistration +import zope.component.event + +from ceo_common.interfaces import IConfig +from ceod.transactions.members import UpdateMemberPositionsTransaction + +from .utils import handle_sync_response, handle_stream_response, print_colon_kv +from ..utils import http_get, http_post + +@click.group(short_help='List or change exec positions') +def positions(): + pass + + +@positions.command(short_help='Get current positions') +def get(): + resp = http_get('/api/positions') + result = handle_sync_response(resp) + print_colon_kv(result.items()) + + +@positions.command(short_help='Update positions') +def update(**kwargs): + click.echo('The positions will be updated:') + print_colon_kv(kwargs.items()) + click.confirm('Do you want to continue?', abort=True) + + resp = http_post('/api/positions', json={k.replace('_', '-'): v for k, v in kwargs.items()}) + handle_stream_response(resp, UpdateMemberPositionsTransaction.operations) + + +# Provides dynamic parameter for update command using config file +@component.provideHandler +@component.adapter(IRegistered) +def _handler(event): + global update + + if not (IUtilityRegistration.providedBy(event.object) and IConfig.providedBy(event.object.component)): + return + + cfg = event.object.component + avail = cfg.get('positions_available') + required = cfg.get('positions_required') + + for pos in avail: + update = click.option(f'--{pos}', metavar='USER', required=(pos in required))(update) diff --git a/ceo/operation_strings.py b/ceo/operation_strings.py index 8ff44f7..2d874e4 100644 --- a/ceo/operation_strings.py +++ b/ceo/operation_strings.py @@ -24,4 +24,7 @@ descriptions = { 'remove_user_from_auxiliary_groups': 'Remove user from auxiliary groups', 'unsubscribe_user_from_auxiliary_mailing_lists': 'Unsubscribe user from auxiliary mailing lists', 'remove_sudo_role': 'Remove sudo role from LDAP', + 'update_positions_ldap': 'Update positions in LDAP', + 'update_exec_group_ldap': 'Update executive group in LDAP', + 'subscribe_to_mailing_lists': 'Subscribe to mailing lists', } diff --git a/ceod/transactions/members/UpdateMemberPositionsTransaction.py b/ceod/transactions/members/UpdateMemberPositionsTransaction.py index 32ac77e..d38400f 100644 --- a/ceod/transactions/members/UpdateMemberPositionsTransaction.py +++ b/ceod/transactions/members/UpdateMemberPositionsTransaction.py @@ -28,7 +28,8 @@ class UpdateMemberPositionsTransaction(AbstractTransaction): # Reverse the dict so it's easier to use (username -> positions) self.positions = defaultdict(list) for position, username in positions_reversed.items(): - self.positions[username].append(position) + if username is not None: + self.positions[username].append(position) # a cached Dict of the Users who need to be modified (username -> User) self.users: Dict[str, IUser] = {} diff --git a/tests/ceo_dev.ini b/tests/ceo_dev.ini index e74895e..978f6f5 100644 --- a/tests/ceo_dev.ini +++ b/tests/ceo_dev.ini @@ -7,3 +7,8 @@ uw_domain = uwaterloo.internal admin_host = phosphoric-acid use_https = false port = 9987 + +[positions] +required = president,vice-president,sysadmin +available = president,vice-president,treasurer,secretary, + sysadmin,cro,librarian,imapd,webmaster,offsck