PgSQL to LDAP transition - Complete

With this commit, the PostgreSQL database is no longer
used by CEO. All term and member information are now
retrieved and saved to the LDAP directory.
This commit is contained in:
Michael Spang 2007-07-16 07:52:21 -04:00
parent 396779cee2
commit 89276f899b
4 changed files with 237 additions and 407 deletions

View File

@ -15,9 +15,9 @@ for key in os.environ.keys():
os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin' os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin'
for pathent in sys.path[:]: #for pathent in sys.path[:]:
if not pathent.find('/usr') == 0: # if not pathent.find('/usr') == 0:
sys.path.remove(pathent) # sys.path.remove(pathent)
euid = os.geteuid() euid = os.geteuid()
egid = os.getegid() egid = os.getegid()
@ -37,7 +37,7 @@ except Exception, e:
sys.exit(1) sys.exit(1)
def usage(): def usage():
print "Usage: ceoquery memberlist|booklist|allmembers|allusers|termusers" print "Usage: ceoquery memberlist|booklist|allusers|termusers"
if len(sys.argv) < 2: if len(sys.argv) < 2:
usage() usage()
@ -46,14 +46,16 @@ elif sys.argv[1] == 'memberlist':
current_term = terms.current() current_term = terms.current()
members = members.list_term(current_term) members = members.list_term(current_term)
for member in members: for member in members.values():
print "%(memberid)s|%(name)s|%(program)s|%(userid)s" % member if 'program' in member:
program = member['program'][0]
else:
program = ''
print "%s|%s|%s" % (member['cn'][0], program, member['uid'][0])
elif sys.argv[1] == 'allmembers': elif sys.argv[1] == 'allmembers':
members = members.list_all() pass
for member in members:
print "%(memberid)s|%(name)s|%(program)s|%(userid)s" % member
elif sys.argv[1] == 'booklist': elif sys.argv[1] == 'booklist':

View File

@ -655,18 +655,14 @@ def remove_member(username, groupname):
### Account Types ### ### Account Types ###
def create_member(username, password, name, memberid): def create_member(username, password, name):
""" """
Creates a UNIX user account with options tailored to CSC members. Creates a UNIX user account with options tailored to CSC members.
Note: The 'other' section of the GECOS field is filled with the CSC
memberid. This section cannot be changed by the user via chfn(1).
Parameters: Parameters:
username - the desired UNIX username username - the desired UNIX username
password - the desired UNIX password password - the desired UNIX password
name - the member's real name name - the member's real name
memberid - the CSC member id number
Exceptions: Exceptions:
InvalidArgument - on bad account attributes provided InvalidArgument - on bad account attributes provided
@ -692,24 +688,20 @@ def create_member(username, password, name, memberid):
maximum_id = cfg['member_max_id'] maximum_id = cfg['member_max_id']
home = cfg['member_home'] + '/' + username home = cfg['member_home'] + '/' + username
description = cfg['member_desc'] description = cfg['member_desc']
gecos_field = build_gecos(name, other=memberid) gecos_field = build_gecos(name)
shell = cfg['member_shell'] shell = cfg['member_shell']
group = cfg['member_group'] group = cfg['member_group']
return create(username, name, minimum_id, maximum_id, home, password, description, gecos_field, shell, group) return create(username, name, minimum_id, maximum_id, home, password, description, gecos_field, shell, group)
def create_club(username, name, memberid): def create_club(username, name):
""" """
Creates a UNIX user account with options tailored to CSC-hosted clubs. Creates a UNIX user account with options tailored to CSC-hosted clubs.
Note: The 'other' section of the GECOS field is filled with the CSC
memberid. This section cannot be changed by the user via chfn(1).
Parameters: Parameters:
username - the desired UNIX username username - the desired UNIX username
name - the club name name - the club name
memberid - the CSC member id number
Exceptions: Exceptions:
InvalidArgument - on bad account attributes provided InvalidArgument - on bad account attributes provided
@ -732,7 +724,7 @@ def create_club(username, name, memberid):
maximum_id = cfg['club_max_id'] maximum_id = cfg['club_max_id']
home = cfg['club_home'] + '/' + username home = cfg['club_home'] + '/' + username
description = cfg['club_desc'] description = cfg['club_desc']
gecos_field = build_gecos(name, other=memberid) gecos_field = build_gecos(name)
shell = cfg['club_shell'] shell = cfg['club_shell']
group = cfg['club_group'] group = cfg['club_group']

View File

@ -11,7 +11,7 @@ must also be moved into this module.
""" """
import re import re
from csc.adm import terms from csc.adm import terms
from csc.backends import db, ldapi from csc.backends import ldapi
from csc.common import conf from csc.common import conf
from csc.common.excep import InvalidArgument from csc.common.excep import InvalidArgument
@ -42,7 +42,6 @@ def load_configuration():
### Exceptions ### ### Exceptions ###
DBException = db.DBException
ConfigurationException = conf.ConfigurationException ConfigurationException = conf.ConfigurationException
class MemberException(Exception): class MemberException(Exception):
@ -87,9 +86,6 @@ class NoSuchMember(MemberException):
### Connection Management ### ### Connection Management ###
# global database connection
db_connection = db.DBConnection()
# global directory connection # global directory connection
ldap_connection = ldapi.LDAPConnection() ldap_connection = ldapi.LDAPConnection()
@ -97,27 +93,25 @@ def connect():
"""Connect to PostgreSQL.""" """Connect to PostgreSQL."""
load_configuration() load_configuration()
db_connection.connect(cfg['server'], cfg['database'])
ldap_connection.connect(cfg['server_url'], cfg['admin_bind_dn'], cfg['admin_bind_pw'], cfg['users_base'], cfg['groups_base']) ldap_connection.connect(cfg['server_url'], cfg['admin_bind_dn'], cfg['admin_bind_pw'], cfg['users_base'], cfg['groups_base'])
def disconnect(): def disconnect():
"""Disconnect from PostgreSQL.""" """Disconnect from PostgreSQL."""
db_connection.disconnect()
ldap_connection.disconnect() ldap_connection.disconnect()
def connected(): def connected():
"""Determine whether the db_connection has been established.""" """Determine whether the connection has been established."""
return db_connection.connected() and ldap_connection.connected() return ldap_connection.connected()
### Member Table ### ### Member Table ###
def new(uid, realname, studentid=None, program=None, mtype='user'): def new(uid, realname, studentid=None, program=None):
""" """
Registers a new CSC member. The member is added to the members table Registers a new CSC member. The member is added to the members table
and registered for the current term. and registered for the current term.
@ -127,23 +121,21 @@ def new(uid, realname, studentid=None, program=None, mtype='user'):
realname - the full real name of the member realname - the full real name of the member
studentid - the student id number of the member studentid - the student id number of the member
program - the program of study of the member program - the program of study of the member
mtype - a string describing the type of member ('user', 'club')
Returns: the memberid of the new member Returns: the username of the new member
Exceptions: Exceptions:
DuplicateStudentID - if the student id already exists in the database DuplicateStudentID - if the student id already exists in the database
InvalidStudentID - if the student id is malformed InvalidStudentID - if the student id is malformed
InvalidRealName - if the real name is malformed InvalidRealName - if the real name is malformed
Example: new("Michael Spang", program="CS") -> 3349 Example: new("Michael Spang", program="CS") -> "mspang"
""" """
# blank attributes should be NULL # blank attributes should be NULL
if studentid == '': studentid = None if studentid == '': studentid = None
if program == '': program = None if program == '': program = None
if uid == '': uid = None if uid == '': uid = None
if mtype == '': mtype = None
# check the student id format # check the student id format
if studentid is not None and not re.match(cfg['studentid_regex'], str(studentid)): if studentid is not None and not re.match(cfg['studentid_regex'], str(studentid)):
@ -154,92 +146,61 @@ def new(uid, realname, studentid=None, program=None, mtype='user'):
raise InvalidRealName(realname) raise InvalidRealName(realname)
# check for duplicate student id # check for duplicate student id
member = db_connection.select_member_by_studentid(studentid) or \ member = ldap_connection.member_search_studentid(studentid)
ldap_connection.member_search_studentid(studentid)
if member: if member:
raise DuplicateStudentID(studentid) raise DuplicateStudentID(studentid)
# check for duplicate userid # check for duplicate userid
member = db_connection.select_member_by_userid(uid) or \ member = ldap_connection.user_lookup(uid)
ldap_connection.user_lookup(uid)
if member: if member:
raise InvalidArgument("uid", uid, "duplicate uid") raise InvalidArgument("uid", uid, "duplicate uid")
# add the member to the database
memberid = db_connection.insert_member(realname, studentid, program, userid=uid)
# add the member to the directory # add the member to the directory
ldap_connection.member_add(uid, realname, studentid, program) ldap_connection.member_add(uid, realname, studentid, program)
# register them for this term in the database
db_connection.insert_term(memberid, terms.current())
# register them for this term in the directory # register them for this term in the directory
member = ldap_connection.member_lookup(uid) member = ldap_connection.member_lookup(uid)
member['term'] = [ terms.current() ] member['term'] = [ terms.current() ]
ldap_connection.user_modify(uid, member) ldap_connection.user_modify(uid, member)
# commit the database transaction return uid
db_connection.commit()
return memberid
def get(memberid): def get(userid):
"""
Look up attributes of a member by memberid.
Returns: a dictionary of attributes
Example: get(3349) -> {
'memberid': 3349,
'name': 'Michael Spang',
'program': 'Computer Science',
...
}
"""
return db_connection.select_member_by_id(memberid)
def get_userid(userid):
""" """
Look up attributes of a member by userid. Look up attributes of a member by userid.
Parameters:
userid - the UNIX user id
Returns: a dictionary of attributes Returns: a dictionary of attributes
Example: get('mspang') -> { Example: get('mspang') -> {
'memberid': 3349, 'cn': [ 'Michael Spang' ],
'name': 'Michael Spang', 'program': [ 'Computer Science' ],
'program': 'Computer Science',
... ...
} }
""" """
return db_connection.select_member_by_userid(userid) return ldap_connection.user_lookup(userid)
def get_studentid(studentid): def get_studentid(studentid):
""" """
Look up attributes of a member by studnetid. Look up attributes of a member by studentid.
Parameters: Parameters:
studentid - the student ID number studentid - the student ID number
Returns: a dictionary of attributes Returns: a dict of members
Example: get(...) -> { Example: get(...) -> {
'memberid': 3349, 'mspang': {
'name': 'Michael Spang', 'name': [ 'Michael Spang' ],
'program': 'Computer Science', 'program': [ 'Computer Science' ],
}
... ...
} }
""" """
return db_connection.select_member_by_studentid(studentid) return ldap_connection.member_search_studentid(studentid)
def list_term(term): def list_term(term):
@ -249,19 +210,16 @@ def list_term(term):
Parameters: Parameters:
term - the term to match members against term - the term to match members against
Returns: a list of member dictionaries Returns: a list of members
Example: list_term('f2006'): -> [ Example: list_term('f2006'): -> {
{ 'memberid': 3349, ... }, 'mspang': { 'cn': 'Michael Spang', ... },
{ 'memberid': ... }. 'ctdalek': { 'cn': 'Calum T. Dalek', ... },
... ...
] }
""" """
# retrieve a list of memberids in term return ldap_connection.member_search_term(term)
memberlist = db_connection.select_members_by_term(term)
return memberlist.values()
def list_name(name): def list_name(name):
@ -273,123 +231,52 @@ def list_name(name):
Returns: a list of member dictionaries Returns: a list of member dictionaries
Example: list_name('Spang'): -> [ Example: list_name('Spang'): -> {
{ 'memberid': 3349, ... }, 'mspang': { 'cn': 'Michael Spang', ... },
{ 'memberid': ... },
... ...
] ]
""" """
# retrieve a list of memberids matching name return ldap_connection.member_search_name(name)
memberlist = db_connection.select_members_by_name(name)
return memberlist.values()
def list_all(): def delete(userid):
"""
Builds a list of all members.
Returns: a list of member dictionaries
"""
# retrieve a list of members
memberlist = db_connection.select_all_members()
return memberlist.values()
def delete(memberid):
""" """
Erase all records of a member. Erase all records of a member.
Note: real members are never removed from the database Note: real members are never removed from the database
Returns: attributes and terms of the member in a tuple Returns: ldap entry of the member
Exceptions: Exceptions:
NoSuchMember - if the member id does not exist NoSuchMember - if the user id does not exist
Example: delete(0) -> ({ 'memberid': 0, name: 'Calum T. Dalek' ...}, ['s1993']) Example: delete('ctdalek') -> { 'cn': [ 'Calum T. Dalek' ], 'term': ['s1993'], ... }
""" """
# save member data # save member data
member = db_connection.select_member_by_id(memberid) member = ldap_connection.user_lookup(userid)
# bail if not found # bail if not found
if not member: if not member:
raise NoSuchMember(memberid) raise NoSuchMember(userid)
term_list = db_connection.select_terms(memberid)
# remove data from the db
db_connection.delete_term_all(memberid)
db_connection.delete_member(memberid)
db_connection.commit()
# remove data from the directory # remove data from the directory
if member and member['userid']: uid = member['uid'][0]
uid = member['userid'] ldap_connection.user_delete(uid)
ldap_connection.user_delete(uid)
return (member, term_list) return member
def update(member):
"""
Update CSC member attributes.
Parameters:
member - a dictionary with member attributes as returned by get,
possibly omitting some attributes. member['memberid']
must exist and be valid. None is NULL.
Exceptions:
NoSuchMember - if the member id does not exist
InvalidStudentID - if the student id number is malformed
DuplicateStudentID - if the student id number exists
Example: update( {'memberid': 3349, userid: 'mspang'} )
"""
if member.has_key('studentid') and member['studentid'] is not None:
studentid = member['studentid']
# check the student id format
if studentid is not None and not re.match(cfg['studentid_regex'], str(studentid)):
raise InvalidStudentID(studentid)
# check for duplicate student id
dupmember = db_connection.select_member_by_studentid(studentid)
if dupmember:
raise DuplicateStudentID(studentid)
# not specifying memberid is a bug
if not member.has_key('memberid'):
raise Exception("no member specified in call to update")
memberid = member['memberid']
# see if member exists
if not get(memberid):
raise NoSuchMember(memberid)
# do the update
db_connection.update_member(member)
# commit the transaction
db_connection.commit()
### Term Table ### ### Term Table ###
def register(memberid, term_list): def register(userid, term_list):
""" """
Registers a member for one or more terms. Registers a member for one or more terms.
Parameters: Parameters:
memberid - the member id number userid - the member's username
term_list - the term to register for, or a list of terms term_list - the term to register for, or a list of terms
Exceptions: Exceptions:
@ -403,13 +290,12 @@ def register(memberid, term_list):
if type(term_list) in (str, unicode): if type(term_list) in (str, unicode):
term_list = [ term_list ] term_list = [ term_list ]
ldap_member = None ldap_member = ldap_connection.member_lookup(userid)
db_member = get(memberid) if ldap_member and 'term' not in ldap_member:
if db_member['userid']: ldap_member['term'] = []
uid = db_member['userid']
ldap_member = ldap_connection.member_lookup(uid) if not ldap_member:
if ldap_member and 'term' not in ldap_member: raise NoSuchMember(userid)
ldap_member['term'] = []
for term in term_list: for term in term_list:
@ -417,52 +303,48 @@ def register(memberid, term_list):
if not re.match('^[wsf][0-9]{4}$', term): if not re.match('^[wsf][0-9]{4}$', term):
raise InvalidTerm(term) raise InvalidTerm(term)
# add term to database
db_connection.insert_term(memberid, term)
# add the term to the directory # add the term to the directory
if ldap_member: ldap_member['term'].append(term)
ldap_member['term'].append(term)
if ldap_member: ldap_connection.user_modify(userid, ldap_member)
ldap_connection.user_modify(uid, ldap_member)
db_connection.commit()
def registered(memberid, term): def registered(userid, term):
""" """
Determines whether a member is registered Determines whether a member is registered
for a term. for a term.
Parameters: Parameters:
memberid - the member id number userid - the member's username
term - the term to check term - the term to check
Returns: whether the member is registered Returns: whether the member is registered
Example: registered(3349, "f2006") -> True Example: registered("mspang", "f2006") -> True
""" """
return db_connection.select_term(memberid, term) is not None member = ldap_connection.member_lookup(userid)
return 'term' in member and term in member['term']
def member_terms(memberid): def member_terms(userid):
""" """
Retrieves a list of terms a member is Retrieves a list of terms a member is
registered for. registered for.
Parameters: Parameters:
memberid - the member id number userid - the member's username
Returns: list of term strings Returns: list of term strings
Example: registered(0) -> 's1993' Example: registered('ctdalek') -> 's1993'
""" """
terms_list = db_connection.select_terms(memberid) member = ldap_connection.member_lookup(userid)
terms_list.sort(terms.compare) if not 'term' in member:
return terms_list return []
else:
return member['term']
@ -484,9 +366,9 @@ if __name__ == '__main__':
tm2usid = '00000002' tm2usid = '00000002'
tm2uprogram = 'Pseudoscience' tm2uprogram = 'Pseudoscience'
tmdict = {'name': tmname, 'userid': tmuid, 'program': tmprogram, 'type': 'user', 'studentid': tmsid } tmdict = {'cn': [tmname], 'uid': [tmuid], 'program': [tmprogram], 'studentid': [tmsid] }
tm2dict = {'name': tm2name, 'userid': tm2uid, 'program': None, 'type': 'user', 'studentid': tm2sid } tm2dict = {'cn': [tm2name], 'uid': [tm2uid], 'studentid': [tm2sid] }
tm2udict = {'name': tm2uname, 'userid': tm2uid, 'program': tm2uprogram, 'type': 'user', 'studentid': tm2usid } tm2udict = {'cn': [tm2uname], 'uid': [tm2uid], 'program': [tm2uprogram], 'studentid': [tm2usid] }
thisterm = terms.current() thisterm = terms.current()
nextterm = terms.next(thisterm) nextterm = terms.next(thisterm)
@ -500,21 +382,17 @@ if __name__ == '__main__':
success() success()
dmid = get_studentid(tmsid) dmid = get_studentid(tmsid)
if dmid: delete(dmid['memberid']) if tmuid in dmid: delete(dmid[tmuid]['uid'][0])
dmid = get_studentid(tm2sid) dmid = get_studentid(tm2sid)
if dmid: delete(dmid['memberid']) if tm2uid in dmid: delete(dmid[tm2uid]['uid'][0])
dmid = get_studentid(tm2usid) dmid = get_studentid(tm2usid)
if dmid: delete(dmid['memberid']) if tm2uid in dmid: delete(dmid[tm2uid]['uid'][0])
test(new) test(new)
tmid = new(tmuid, tmname, tmsid, tmprogram) tmid = new(tmuid, tmname, tmsid, tmprogram)
tm2id = new(tm2uid, tm2name, tm2sid) tm2id = new(tm2uid, tm2name, tm2sid)
success() success()
tmdict['memberid'] = tmid
tm2dict['memberid'] = tm2id
tm2udict['memberid'] = tm2id
test(registered) test(registered)
assert_equal(True, registered(tmid, thisterm)) assert_equal(True, registered(tmid, thisterm))
assert_equal(True, registered(tm2id, thisterm)) assert_equal(True, registered(tm2id, thisterm))
@ -522,19 +400,19 @@ if __name__ == '__main__':
success() success()
test(get) test(get)
assert_equal(tmdict, get(tmid)) tmp = get(tmid)
assert_equal(tm2dict, get(tm2id)) del tmp['objectClass']
del tmp['term']
assert_equal(tmdict, tmp)
tmp = get(tm2id)
del tmp['objectClass']
del tmp['term']
assert_equal(tm2dict, tmp)
success() success()
test(list_name) test(list_name)
assert_equal(True, tmid in [ x['memberid'] for x in list_name(tmname) ]) assert_equal(True, tmid in list_name(tmname).keys())
assert_equal(True, tm2id in [ x['memberid'] for x in list_name(tm2name) ]) assert_equal(True, tm2id in list_name(tm2name).keys())
success()
test(list_all)
allmembers = list_all()
assert_equal(True, tmid in [ x['memberid'] for x in allmembers ])
assert_equal(True, tm2id in [ x['memberid'] for x in allmembers ])
success() success()
test(register) test(register)
@ -548,24 +426,28 @@ if __name__ == '__main__':
success() success()
test(list_term) test(list_term)
assert_equal(True, tmid in [ x['memberid'] for x in list_term(thisterm) ]) assert_equal(True, tmid in list_term(thisterm).keys())
assert_equal(True, tmid in [ x['memberid'] for x in list_term(nextterm) ]) assert_equal(True, tmid in list_term(nextterm).keys())
assert_equal(True, tm2id in [ x['memberid'] for x in list_term(thisterm) ]) assert_equal(True, tm2id in list_term(thisterm).keys())
assert_equal(False, tm2id in [ x['memberid'] for x in list_term(nextterm) ]) assert_equal(False, tm2id in list_term(nextterm).keys())
success() success()
test(update) test(get)
update(tm2udict) tmp = get(tm2id)
assert_equal(tm2udict, get(tm2id)) del tmp['objectClass']
success() del tmp['term']
assert_equal(tm2dict, tmp)
test(get_userid)
assert_equal(tm2udict, get_userid(tm2uid))
success() success()
test(get_studentid) test(get_studentid)
assert_equal(tm2udict, get_studentid(tm2usid)) tmp = get_studentid(tm2sid)[tm2uid]
assert_equal(tmdict, get_studentid(tmsid)) del tmp['objectClass']
del tmp['term']
assert_equal(tm2dict, tmp)
tmp = get_studentid(tmsid)[tmuid]
del tmp['objectClass']
del tmp['term']
assert_equal(tmdict, tmp)
success() success()
test(delete) test(delete)

View File

@ -18,21 +18,56 @@ from csc.common.excep import InvalidArgument
BORDER_COLOR = curses.COLOR_RED BORDER_COLOR = curses.COLOR_RED
def read_uid(wnd):
"""Read a username."""
prompt = 'Username:'
return inputbox(wnd, prompt, 36)
def read_member(wnd):
"""Looks up a member."""
# connect the members module to its backend if necessary
if not members.connected(): members.connect()
uid = read_uid(wnd)
if not uid or uid.lower() == 'exit':
return
member = members.get(uid)
if not member:
msgbox(wnd, "Invalid username: %s" % uid)
return
# display user
display_member_details(wnd, member)
return member
def action_library(wnd):
"""Display a link to the library."""
msgbox(wnd, "Please visit library.csclub.uwaterloo.ca")
def action_new_member(wnd): def action_new_member(wnd):
"""Interactively add a new member.""" """Interactively add a new member."""
userid, studentid, program = '', None, '' userid, studentid, program = '', None, ''
msgbox(wnd, "Membership is $2.00 CDN. Please ensure\n"
"the money is desposited in the safe\n"
"before continuing.")
# read the name # read the name
prompt = " Name: " prompt = "New member's full name: "
realname = inputbox(wnd, prompt, 18) realname = inputbox(wnd, prompt, 18)
# abort if no username is entered # abort if no name is entered
if not realname or realname.lower() == 'exit': if not realname or realname.lower() == 'exit':
return False return False
# read the student id # read the student id
prompt = "Student id:" prompt = "New member's student ID:"
while studentid is None or (re.search("[^0-9]", studentid) and not studentid.lower() == 'exit'): while studentid is None or (re.search("[^0-9]", studentid) and not studentid.lower() == 'exit'):
studentid = inputbox(wnd, prompt, 18) studentid = inputbox(wnd, prompt, 18)
@ -44,7 +79,7 @@ def action_new_member(wnd):
studentid = None studentid = None
# read the program of study # read the program of study
prompt = " Program:" prompt = "New member's program of study:"
program = inputbox(wnd, prompt, 18) program = inputbox(wnd, prompt, 18)
# abort if exit is entered # abort if exit is entered
@ -52,7 +87,7 @@ def action_new_member(wnd):
return False return False
# read user id # read user id
prompt = "Userid:" prompt = "New member's UWdir username:"
while userid == '': while userid == '':
userid = inputbox(wnd, prompt, 18) userid = inputbox(wnd, prompt, 18)
@ -65,10 +100,10 @@ def action_new_member(wnd):
# attempt to create the member # attempt to create the member
try: try:
memberid = members.new(userid, realname, studentid, program) members.new(userid, realname, studentid, program)
msgbox(wnd, "Success! Your memberid is %s. You are now registered\n" msgbox(wnd, "Success! Your username is %s. You are now registered\n"
% memberid + "for the " + terms.current() + " term.") % userid + "for the " + terms.current() + " term.")
except members.InvalidStudentID: except members.InvalidStudentID:
msgbox(wnd, "Invalid student ID: %s" % studentid) msgbox(wnd, "Invalid student ID: %s" % studentid)
@ -90,26 +125,25 @@ def action_new_member(wnd):
def action_term_register(wnd): def action_term_register(wnd):
"""Interactively register a member for a term.""" """Interactively register a member for a term."""
memberid, term = '', '' term = ''
# read the member id member = read_member(wnd)
prompt = 'Enter memberid ("exit" to cancel):' if not member:
memberuserid = inputbox(wnd, prompt, 36) return False
uid = member['uid'][0]
if not memberuserid or memberuserid.lower() == 'exit': # verify member
prompt = "Is this the correct member?"
answer = None
while answer != "yes" and answer != "y" and answer != "n" and answer != "no" and answer != "exit":
answer = inputbox(wnd, prompt, 28)
# user abort
if answer == "exit":
return False return False
member = get_member_memberid_userid(wnd, memberuserid)
if not member: return False
memberid = member['memberid']
term_list = members.member_terms(memberid)
# display user
display_member_details(wnd, member, term_list)
# read the term # read the term
prompt = "Which term to register for ([fws]20nn):" prompt = "Which term to register for ([wsf]20nn):"
while not re.match('^[wsf][0-9]{4}$', term) and not term == 'exit': while not re.match('^[wsf][0-9]{4}$', term) and not term == 'exit':
term = inputbox(wnd, prompt, 41) term = inputbox(wnd, prompt, 41)
@ -118,17 +152,17 @@ def action_term_register(wnd):
return False return False
# already registered? # already registered?
if members.registered(memberid, term): if members.registered(uid, term):
msgbox(wnd, "You are already registered for term " + term) msgbox(wnd, "You are already registered for term " + term)
return False return False
try: try:
# attempt to register # attempt to register
members.register(memberid, term) members.register(uid, term)
# display success message [sic] # display success message
msgbox(wnd, "Your are now registered for term " + term) msgbox(wnd, "You are now registered for term " + term)
except members.InvalidTerm: except members.InvalidTerm:
msgbox(wnd, "Term is not valid: %s" % term) msgbox(wnd, "Term is not valid: %s" % term)
@ -139,23 +173,22 @@ def action_term_register(wnd):
def action_term_register_multiple(wnd): def action_term_register_multiple(wnd):
"""Interactively register a member for multiple terms.""" """Interactively register a member for multiple terms."""
memberid, base, num = '', '', None base, num = '', None
# read the member id member = read_member(wnd)
prompt = 'Enter memberid ("exit" to cancel):' if not member:
memberuserid = inputbox(wnd, prompt, 36)
if not memberuserid or memberuserid.lower() == 'exit':
return False return False
uid = member['uid'][0]
member = get_member_memberid_userid(wnd, memberuserid) # verify member
if not member: return False prompt = "Is this the correct member?"
answer = None
while answer != "yes" and answer != "y" and answer != "n" and answer != "no" and answer != "exit":
answer = inputbox(wnd, prompt, 28)
memberid = member['memberid'] # user abort
term_list = members.member_terms(memberid) if answer == "exit":
return False
# display user
display_member_details(wnd, member, term_list)
# read the base # read the base
prompt = "Which term to start registering ([fws]20nn):" prompt = "Which term to start registering ([fws]20nn):"
@ -182,14 +215,14 @@ def action_term_register_multiple(wnd):
# already registered? # already registered?
for term in term_list: for term in term_list:
if members.registered(memberid, term): if members.registered(uid, term):
msgbox(wnd, "You are already registered for term " + term) msgbox(wnd, "You are already registered for term " + term)
return False return False
try: try:
# attempt to register all terms # attempt to register all terms
members.register(memberid, term_list) members.register(uid, term_list)
# display success message [sic] # display success message [sic]
msgbox(wnd, "Your are now registered for terms: " + ", ".join(term_list)) msgbox(wnd, "Your are now registered for terms: " + ", ".join(term_list))
@ -200,30 +233,6 @@ def action_term_register_multiple(wnd):
return False return False
def repair_account(wnd, memberid, userid):
"""Attemps to repair an account."""
if not accounts.connected(): accounts.connect()
member = members.get(memberid)
exists, haspw = accounts.status(userid)
if not exists:
password = input_password(wnd)
accounts.create_member(userid, password, member['name'], memberid)
msgbox(wnd, "Account created (where the hell did it go, anyway?)\n"
"If your homedir still exists, it will not be inaccessible to you,\n"
"please contact systems-committee@csclub.uwaterloo.ca to get this resolved.\n")
elif not haspw:
password = input_password(wnd)
accounts.add_password(userid, password)
msgbox(wnd, "Password added to account.")
else:
msgbox(wnd, "No problems to repair.")
def input_password(wnd): def input_password(wnd):
# password input loop # password input loop
@ -249,24 +258,16 @@ def input_password(wnd):
def action_create_account(wnd): def action_create_account(wnd):
"""Interactively create an account for a member.""" """Interactively create an account for a member."""
memberid, userid = '', '' member = read_member(wnd)
if not member:
# read the member id
prompt = 'Enter member ID (exit to cancel):'
memberid = inputbox(wnd, prompt, 35)
if not memberid or memberid.lower() == 'exit':
return False return False
member = get_member_memberid_userid(wnd, memberid) # member already has an account?
if not member: return False if not accounts.connected(): accounts.connect()
if 'posixAccount' in member['objectClass']:
msgbox(wnd, "Account already exists.")
return False
memberid = member['memberid']
term_list = members.member_terms(memberid)
# display the member
display_member_details(wnd, member, term_list)
# verify member # verify member
prompt = "Is this the correct member?" prompt = "Is this the correct member?"
answer = None answer = None
@ -282,30 +283,9 @@ def action_create_account(wnd):
msgbox(wnd, "I suggest searching for the member by userid or name from the main menu.") msgbox(wnd, "I suggest searching for the member by userid or name from the main menu.")
return False return False
# member already has an account? msgbox(wnd, "Ensure the member has signed the machine\n"
if not accounts.connected(): accounts.connect() "usage policy. Accounts of users who have\n"
if member['userid'] and accounts.status(member['userid'])[0]: "not signed will be suspended if discovered.")
userid = member['userid']
msgbox(wnd, "Member " + str(memberid) + " already has an account: " + member['userid'] + "\n"
"Attempting to repair it. Contact the sysadmin if there are still problems." )
repair_account(wnd, memberid, userid)
return False
if member['userid']:
userid = member['userid']
# read user id
prompt = "Userid:"
while userid == '':
userid = inputbox(wnd, prompt, 18)
# user abort
if userid is None or userid.lower() == 'exit':
return False
# read password # read password
password = input_password(wnd) password = input_password(wnd)
@ -313,7 +293,7 @@ def action_create_account(wnd):
# create the UNIX account # create the UNIX account
try: try:
if not accounts.connected(): accounts.connect() if not accounts.connected(): accounts.connect()
accounts.create_member(userid, password, member['name'], memberid) accounts.create_member(member['uid'][0], password, member['cn'][0])
except accounts.NameConflict, e: except accounts.NameConflict, e:
msgbox(wnd, str(e)) msgbox(wnd, str(e))
return False return False
@ -329,86 +309,53 @@ def action_create_account(wnd):
except accounts.KrbException, e: except accounts.KrbException, e:
msgbox(wnd, "Error creating Kerberos principal - Contact the Systems Administrator: %s" % e) msgbox(wnd, "Error creating Kerberos principal - Contact the Systems Administrator: %s" % e)
return False return False
# now update the CEO database with the username
members.update( {'memberid': memberid, 'userid': userid} )
# success # success
msgbox(wnd, "Please run 'addhomedir " + userid + "'.") msgbox(wnd, "Please run 'addhomedir " + member['uid'][0] + "'.")
msgbox(wnd, "Success! Your account has been added") msgbox(wnd, "Success! Your account has been added")
return False return False
def display_member_details(wnd, member, term_list): def display_member_details(wnd, member):
"""Display member attributes in a message box.""" """Display member attributes in a message box."""
# clone and sort term_list # clone and sort term_list
term_list = list(term_list) if 'term' in member:
term_list = list(member['term'])
else:
term_list = []
term_list.sort( terms.compare ) term_list.sort( terms.compare )
# labels for data # labels for data
id_label, studentid_label, name_label = "ID:", "StudentID:", "Name:" id_label, studentid_label, name_label = "ID:", "StudentID:", "Name:"
program_label, userid_label, terms_label = "Program:", "User ID:", "Terms:" program_label, terms_label = "Program:", "Terms:"
if 'program' in member:
program = member['program'][0]
else:
program = None
if 'studentid' in member:
studentid = member['studentid'][0]
else:
studentid = None
# format it all into a massive string # format it all into a massive string
message = "%8s %-20s %10s %-10s (user)\n" % (name_label, member['name'], id_label, member['memberid']) + \ message = "%8s %-20s %10s %-10s\n" % (name_label, member['cn'][0], id_label, member['uid'][0]) + \
"%8s %-20s %10s %-10s\n" % (program_label, member['program'], studentid_label, member['studentid']) "%8s %-20s %10s %-10s\n" % (program_label, program, studentid_label, studentid)
if member['userid']:
message += "%8s %s\n" % (userid_label, member['userid'])
else:
message += 'No user ID.\n'
message += "%s %s" % (terms_label, " ".join(term_list)) message += "%s %s" % (terms_label, " ".join(term_list))
# display the string in a message box # display the string in a message box
msgbox(wnd, message) msgbox(wnd, message)
def get_member_memberid_userid(wnd, memberuserid):
"""Retrieve member attributes by member of user id."""
# connect the members module to its backends
if not members.connected(): members.connect()
# retrieve member data
if re.match('^[0-9]*$', memberuserid):
# numeric memberid, look it up
memberid = int(memberuserid)
member = members.get( memberid )
if not member:
msgbox(wnd, '%s is an invalid memberid' % memberuserid)
else:
# non-numeric memberid: try userids
member = members.get_userid( memberuserid )
if not member:
msgbox(wnd, "%s is an invalid account userid" % memberuserid)
return member
def action_display_member(wnd): def action_display_member(wnd):
"""Interactively display a member.""" """Interactively display a member."""
# read the member id if not members.connected(): members.connect()
prompt = 'Memberid: ' member = read_member(wnd)
memberid = inputbox(wnd, prompt, 36)
if not memberid or memberid.lower() == 'exit':
return False
member = get_member_memberid_userid(wnd, memberid)
if not member: return False
term_list = members.member_terms( member['memberid'] )
# display the details in a window
display_member_details(wnd, member, term_list)
return False return False
@ -430,14 +377,26 @@ def format_members(member_list):
# clone and sort member_list # clone and sort member_list
member_list = list(member_list) member_list = list(member_list)
member_list.sort( lambda x, y: x['memberid']-y['memberid'] ) member_list.sort( lambda x, y: cmp(x['uid'], y['uid']) )
buf = '' buf = ''
for member in member_list: for member in member_list:
attrs = ( member['memberid'], member['name'], member['studentid'], if 'uid' in member:
member['type'], member['program'], member['userid'] ) uid = member['uid'][0]
buf += "%4d %50s %10s %10s \n%55s %10s\n\n" % attrs else:
uid = None
if 'program' in member:
program = member['program'][0]
else:
program = None
if 'studentid' in member:
studentid = member['studentid'][0]
else:
studentid = None
attrs = ( uid, member['cn'][0],
studentid, program )
buf += "%10s %30s %10s\n%41s\n\n" % attrs
return buf return buf
@ -463,7 +422,7 @@ def action_list_term(wnd):
member_list = members.list_term(term) member_list = members.list_term(term)
# format the data into a mess of text # format the data into a mess of text
buf = format_members(member_list) buf = format_members(member_list.values())
# display the mass of text with a pager # display the mass of text with a pager
page( buf ) page( buf )
@ -491,7 +450,7 @@ def action_list_name(wnd):
member_list = members.list_name(name) member_list = members.list_name(name)
# format the data into a mess of text # format the data into a mess of text
buf = format_members(member_list) buf = format_members(member_list.values())
# display the mass of text with a pager # display the mass of text with a pager
page( buf ) page( buf )
@ -516,14 +475,10 @@ def action_list_studentid(wnd):
if not members.connected(): members.connect() if not members.connected(): members.connect()
# retrieve a list of members for term # retrieve a list of members for term
member = members.get_studentid(studentid) member_list = members.get_studentid(studentid)
if member != None:
member_list = [ members.get_studentid(studentid) ]
else:
member_list = []
# format the data into a mess of text # format the data into a mess of text
buf = format_members(member_list) buf = format_members(member_list.values())
# display the mass of text with a pager # display the mass of text with a pager
page( buf ) page( buf )
@ -551,8 +506,7 @@ top_menu = [
( "Search for a member by name", action_list_name ), ( "Search for a member by name", action_list_name ),
( "Search for a member by student id", action_list_studentid ), ( "Search for a member by student id", action_list_studentid ),
( "Create an account", action_create_account ), ( "Create an account", action_create_account ),
( "Re Create an account", action_create_account ), ( "Library functions", action_library ),
( "Library functions", null_callback ),
( "Exit", exit_callback ), ( "Exit", exit_callback ),
] ]