diff --git a/ceo_common/interfaces/IUser.py b/ceo_common/interfaces/IUser.py index 42674a5..2ebdcb9 100644 --- a/ceo_common/interfaces/IUser.py +++ b/ceo_common/interfaces/IUser.py @@ -36,11 +36,6 @@ class IUser(Interface): Returns False if this is the Unix user for a member. """ - def should_be_club_rep(self) -> bool: - """ - Returns True iff this user's most recent term was a non-member term. - """ - def add_to_ldap(): """ Add a new record to LDAP for this user. diff --git a/ceod/model/User.py b/ceod/model/User.py index fd1f833..d5db444 100644 --- a/ceod/model/User.py +++ b/ceod/model/User.py @@ -6,10 +6,10 @@ import ldap3 from zope import component from zope.interface import implementer +from .utils import should_be_club_rep from .validators import is_valid_shell, is_valid_term from ceo_common.interfaces import ILDAPService, IKerberosService, IFileService, \ IUser, IConfig, IMailmanService -from ceo_common.model import Term @implementer(IUser) @@ -55,7 +55,11 @@ class User: self.mail_local_addresses = mail_local_addresses or [] self._is_club = is_club if is_club_rep is None: - self.is_club_rep = self.should_be_club_rep() + if is_club: + # not a real user + self.is_club_rep = False + else: + self.is_club_rep = should_be_club_rep(terms, non_member_terms) else: self.is_club_rep = is_club_rep self.ldap3_entry = ldap3_entry @@ -93,19 +97,6 @@ class User: def is_club(self) -> bool: return self._is_club - def should_be_club_rep(self) -> bool: - if self._is_club: - # not a real user - return False - if not self.terms: - # no member terms => was only ever a club rep - return True - if not self.non_member_terms: - # no non-member terms => was only ever a member - return False - # decide using the most recent term (member or non-member) - return max(map(Term, self.non_member_terms)) > max(map(Term, self.terms)) - def add_to_ldap(self): self.ldap_srv.add_user(self) diff --git a/ceod/model/utils.py b/ceod/model/utils.py index 80610c3..6b215f7 100644 --- a/ceod/model/utils.py +++ b/ceod/model/utils.py @@ -1,4 +1,6 @@ -from typing import Dict, List +from typing import Dict, List, Union + +from ceo_common.model import Term def bytes_to_strings(data: Dict[str, List[bytes]]) -> Dict[str, List[str]]: @@ -25,3 +27,16 @@ def dn_to_uid(dn: str) -> str: -> 'ctdalek' """ return dn.split(',', 1)[0].split('=')[1] + + +def should_be_club_rep(terms: Union[None, List[str]], + non_member_terms: Union[None, List[str]]) -> bool: + """Returns True iff a user's most recent term was a non-member term.""" + if not non_member_terms: + # no non-member terms => was only ever a member + return False + if not terms: + # no member terms => was only ever a club rep + return True + # decide using the most recent term (member or non-member) + return max(map(Term, non_member_terms)) > max(map(Term, terms)) diff --git a/one_time_scripts/is_club_rep.py b/one_time_scripts/is_club_rep.py new file mode 100644 index 0000000..8de1ab8 --- /dev/null +++ b/one_time_scripts/is_club_rep.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +""" +This is a script which adds the isClubRep attribute to all LDAP user records +whose most recent nonMemberTerm is later than their most recent (member) term. + +GSSAPI is used for LDAP 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 + +sys.path.append(os.getcwd()) +from ceod.model.utils import should_be_club_rep + +# modify as necessary +LDAP_URI = "ldaps://auth1.csclub.uwaterloo.ca" +LDAP_MEMBERS_BASE = "ou=People,dc=csclub,dc=uwaterloo,dc=ca" + +conn = ldap3.Connection( + LDAP_URI, authentication=ldap3.SASL, sasl_mechanism=ldap3.KERBEROS, + auto_bind=True, raise_exceptions=True) +conn.search(LDAP_MEMBERS_BASE, '(objectClass=member)', + attributes=['uid', 'isClubRep', 'term', 'nonMemberTerm']) +total_records_updated = 0 +for entry in conn.entries: + if not should_be_club_rep(entry.term.values, entry.nonMemberTerm.values): + continue + if entry.isClubRep.value: + continue + changes = {'isClubRep': [(ldap3.MODIFY_REPLACE, [True])]} + conn.modify(entry.entry_dn, changes) + print('Modified %s' % entry.uid.value) + total_records_updated += 1 +print('Total records updated: %d' % total_records_updated)