From 7a13a70eae4eab4c063b7633a03d0d18f7d179ff Mon Sep 17 00:00:00 2001 From: Rio Liu Date: Mon, 16 May 2022 21:15:08 -0400 Subject: [PATCH 1/3] unsubscribe from csc general when shadow expiring member, and resubscribe when renewed --- ceod/api/members.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ceod/api/members.py b/ceod/api/members.py index 7013782..1398996 100644 --- a/ceod/api/members.py +++ b/ceod/api/members.py @@ -92,14 +92,22 @@ def renew_user(username: str): g.need_admin_creds = True ldap_srv = component.getUtility(ILDAPService) + cfg = component.getUtility(IConfig) user = ldap_srv.get_user(username) + member_list = cfg.get('mailman3_new_member_list') + + def unexpire(user): + if user.shadowExpire: + user.set_expired(False) + user.subscribe_to_mailing_list(member_list) + if body.get('terms'): user.add_terms(body['terms']) - user.set_expired(False) + unexpire(user) return {'terms_added': body['terms']} elif body.get('non_member_terms'): user.add_non_member_terms(body['non_member_terms']) - user.set_expired(False) + unexpire(user) return {'non_member_terms_added': body['non_member_terms']} else: raise BadRequest('Must specify either terms or non-member terms') @@ -129,10 +137,13 @@ def expire_users(): dry_run = is_truthy(request.args.get('dry_run', 'false')) ldap_srv = component.getUtility(ILDAPService) + cfg = component.getUtility(IConfig) members = ldap_srv.get_expiring_users() + member_list = cfg.get('mailman3_new_member_list') if not dry_run: for member in members: member.set_expired(True) + member.unsubscribe_from_mailing_list(member_list) return json.jsonify([member.uid for member in members]) -- 2.39.2 From c47d070072f1b67fb89cea81b996a379fb2e369b Mon Sep 17 00:00:00 2001 From: Rio6 Date: Mon, 16 May 2022 21:52:55 -0400 Subject: [PATCH 2/3] ignore subscription errors when sub/unsubbing for expiring members --- ceod/api/members.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ceod/api/members.py b/ceod/api/members.py index 1398996..ee34bb2 100644 --- a/ceod/api/members.py +++ b/ceod/api/members.py @@ -4,8 +4,8 @@ from zope import component from .utils import authz_restrict_to_staff, authz_restrict_to_syscom, \ user_is_in_group, requires_authentication_no_realm, \ create_streaming_response, development_only, is_truthy -from ceo_common.errors import BadRequest -from ceo_common.interfaces import ILDAPService +from ceo_common.errors import BadRequest, UserAlreadySubscribedError, UserNotSubscribedError +from ceo_common.interfaces import ILDAPService, IConfig from ceod.transactions.members import ( AddMemberTransaction, ModifyMemberTransaction, @@ -99,7 +99,10 @@ def renew_user(username: str): def unexpire(user): if user.shadowExpire: user.set_expired(False) - user.subscribe_to_mailing_list(member_list) + try: + user.subscribe_to_mailing_list(member_list) + except UserAlreadySubscribedError: + pass if body.get('terms'): user.add_terms(body['terms']) @@ -144,6 +147,9 @@ def expire_users(): if not dry_run: for member in members: member.set_expired(True) - member.unsubscribe_from_mailing_list(member_list) + try: + member.unsubscribe_from_mailing_list(member_list) + except UserNotSubscribedError: + pass return json.jsonify([member.uid for member in members]) -- 2.39.2 From 3d3043ce6b27ed615e77e5244febaf3469af561f Mon Sep 17 00:00:00 2001 From: Max Erenberg Date: Thu, 2 Jun 2022 01:49:40 -0400 Subject: [PATCH 3/3] add one-time script --- .../unsubscribe_expired_members.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100755 one_time_scripts/unsubscribe_expired_members.py diff --git a/one_time_scripts/unsubscribe_expired_members.py b/one_time_scripts/unsubscribe_expired_members.py new file mode 100755 index 0000000..ccfc491 --- /dev/null +++ b/one_time_scripts/unsubscribe_expired_members.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +""" +This is a script which unsubscribes expired members from csc-general. + +GSSAPI is used for SPNEGO authentication, so make sure to run `kinit` first. +Also, make sure to run this script from the top-level of the git directory +(see the sys.path hack below). +""" +import os +import sys + +import ldap3 +from zope import component + +sys.path.append(os.getcwd()) +from ceo_common.errors import UserNotSubscribedError +from ceo_common.interfaces import IConfig, IHTTPClient +from ceo_common.model import Config, HTTPClient, RemoteMailmanService + +# modify as necessary +CONFIG_FILE = '/etc/csc/ceod.ini' +NEW_MEMBER_LIST = 'csc-general' + +cfg = Config(CONFIG_FILE) +component.provideUtility(cfg, IConfig) +http_client = HTTPClient() +component.provideUtility(http_client, IHTTPClient) +mailman_srv = RemoteMailmanService() +LDAP_URI = cfg.get('ldap_server_url') +LDAP_MEMBERS_BASE = cfg.get('ldap_users_base') + +conn = ldap3.Connection(LDAP_URI, auto_bind=True, raise_exceptions=True) +conn.search(LDAP_MEMBERS_BASE, '(shadowExpire=1)', attributes=['uid']) +total_unsubscribed = 0 +for entry in conn.entries: + uid = entry.uid.value + try: + mailman_srv.unsubscribe(uid, NEW_MEMBER_LIST) + print(f'Unsubscribed {uid}') + total_unsubscribed += 1 + except UserNotSubscribedError: + print(f'{uid} is already unsubscribed') +print(f'Total unsubscribed: {total_unsubscribed}') -- 2.39.2