Merge branch 'ldap'
This commit is contained in:
commit
6ab575ec9e
|
@ -0,0 +1,22 @@
|
||||||
|
# CSC Member Information Schema
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.27934.1.1.1 NAME 'term'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{5} )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.27934.1.1.2 NAME 'program'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{1024} SINGLE-VALUE )
|
||||||
|
|
||||||
|
attributetype ( 1.3.6.1.4.1.27934.1.1.3 NAME 'studentid'
|
||||||
|
EQUALITY caseIgnoreIA5Match
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{8} SINGLE-VALUE )
|
||||||
|
|
||||||
|
objectclass ( 1.3.6.1.4.1.27934.1.2.1 NAME 'member'
|
||||||
|
SUP top AUXILIARY
|
||||||
|
MUST ( cn $ uid $ studentid )
|
||||||
|
MAY ( program $ term $ description ) )
|
||||||
|
|
||||||
|
objectclass ( 1.3.6.1.4.1.27934.1.2.2 NAME 'club'
|
||||||
|
SUP top AUXILIARY
|
||||||
|
MUST ( cn $ uid ) )
|
|
@ -190,8 +190,15 @@ def create(username, name, minimum_id, maximum_id, home, password=None, descript
|
||||||
|
|
||||||
### User creation ###
|
### User creation ###
|
||||||
|
|
||||||
# create the LDAP entry
|
if not ldap_connection.user_lookup(username):
|
||||||
ldap_connection.user_add(username, name, userid, gid, home, shell, gecos, description)
|
|
||||||
|
# create the LDAP entry
|
||||||
|
ldap_connection.account_add(username, name, userid, gid, home, shell, gecos, description)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
# add the required attribute to the LDAP entry
|
||||||
|
ldap_connection.member_add_account(username, userid, gid, home, shell, gecos)
|
||||||
|
|
||||||
# create a user group if no other group was specified
|
# create a user group if no other group was specified
|
||||||
if not group:
|
if not group:
|
||||||
|
@ -257,7 +264,7 @@ def status(username):
|
||||||
Returns: a boolean 2-tuple (exists, has_password)
|
Returns: a boolean 2-tuple (exists, has_password)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ldap_state = ldap_connection.user_lookup(username)
|
ldap_state = ldap_connection.account_lookup(username)
|
||||||
krb_state = krb_connection.get_principal(username)
|
krb_state = krb_connection.get_principal(username)
|
||||||
return (ldap_state is not None, krb_state is not None)
|
return (ldap_state is not None, krb_state is not None)
|
||||||
|
|
||||||
|
@ -786,9 +793,9 @@ def check_name_usage(name):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# see if user exists in LDAP
|
# see if user exists in LDAP
|
||||||
if ldap_connection.user_lookup(name):
|
if ldap_connection.account_lookup(name):
|
||||||
raise NameConflict(name, "account", "LDAP")
|
raise NameConflict(name, "account", "LDAP")
|
||||||
|
|
||||||
# see if group exists in LDAP
|
# see if group exists in LDAP
|
||||||
if ldap_connection.group_lookup(name):
|
if ldap_connection.group_lookup(name):
|
||||||
raise NameConflict(name, "group", "LDAP")
|
raise NameConflict(name, "group", "LDAP")
|
||||||
|
@ -815,10 +822,10 @@ def check_name_usage(name):
|
||||||
|
|
||||||
def check_account_status(username, require_ldap=True, require_krb=False):
|
def check_account_status(username, require_ldap=True, require_krb=False):
|
||||||
"""Helper function to verify that an account exists."""
|
"""Helper function to verify that an account exists."""
|
||||||
|
|
||||||
if not connected():
|
if not connected():
|
||||||
raise AccountException("Not connected to LDAP and Kerberos")
|
raise AccountException("Not connected to LDAP and Kerberos")
|
||||||
if require_ldap and not ldap_connection.user_lookup(username):
|
if require_ldap and not ldap_connection.account_lookup(username):
|
||||||
raise NoSuchAccount(username, "LDAP")
|
raise NoSuchAccount(username, "LDAP")
|
||||||
if require_krb and not krb_connection.get_principal(username):
|
if require_krb and not krb_connection.get_principal(username):
|
||||||
raise NoSuchAccount(username, "KRB")
|
raise NoSuchAccount(username, "KRB")
|
||||||
|
|
|
@ -11,8 +11,9 @@ 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
|
from csc.backends import db, ldapi
|
||||||
from csc.common import conf
|
from csc.common import conf
|
||||||
|
from csc.common.excep import InvalidArgument
|
||||||
|
|
||||||
|
|
||||||
### Configuration ###
|
### Configuration ###
|
||||||
|
@ -25,7 +26,8 @@ def load_configuration():
|
||||||
"""Load Members Configuration"""
|
"""Load Members Configuration"""
|
||||||
|
|
||||||
string_fields = [ 'studentid_regex', 'realname_regex', 'server',
|
string_fields = [ 'studentid_regex', 'realname_regex', 'server',
|
||||||
'database', 'user', 'password' ]
|
'database', 'user', 'password', 'server_url', 'users_base',
|
||||||
|
'groups_base', 'admin_bind_dn', 'admin_bind_pw' ]
|
||||||
|
|
||||||
# read configuration file
|
# read configuration file
|
||||||
cfg_tmp = conf.read(CONFIG_FILE)
|
cfg_tmp = conf.read(CONFIG_FILE)
|
||||||
|
@ -86,41 +88,46 @@ class NoSuchMember(MemberException):
|
||||||
### Connection Management ###
|
### Connection Management ###
|
||||||
|
|
||||||
# global database connection
|
# global database connection
|
||||||
connection = db.DBConnection()
|
db_connection = db.DBConnection()
|
||||||
|
|
||||||
|
# global directory connection
|
||||||
|
ldap_connection = ldapi.LDAPConnection()
|
||||||
|
|
||||||
def connect():
|
def connect():
|
||||||
"""Connect to PostgreSQL."""
|
"""Connect to PostgreSQL."""
|
||||||
|
|
||||||
load_configuration()
|
load_configuration()
|
||||||
connection.connect(cfg['server'], cfg['database'])
|
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'])
|
||||||
|
|
||||||
|
|
||||||
def disconnect():
|
def disconnect():
|
||||||
"""Disconnect from PostgreSQL."""
|
"""Disconnect from PostgreSQL."""
|
||||||
|
|
||||||
connection.disconnect()
|
db_connection.disconnect()
|
||||||
|
ldap_connection.disconnect()
|
||||||
|
|
||||||
|
|
||||||
def connected():
|
def connected():
|
||||||
"""Determine whether the connection has been established."""
|
"""Determine whether the db_connection has been established."""
|
||||||
|
|
||||||
return connection.connected()
|
return db_connection.connected() and ldap_connection.connected()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Member Table ###
|
### Member Table ###
|
||||||
|
|
||||||
def new(realname, studentid=None, program=None, mtype='user', userid=None):
|
def new(uid, realname, studentid=None, program=None, mtype='user'):
|
||||||
"""
|
"""
|
||||||
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.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
uid - the initial user id
|
||||||
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')
|
mtype - a string describing the type of member ('user', 'club')
|
||||||
userid - the initial user id
|
|
||||||
|
|
||||||
Returns: the memberid of the new member
|
Returns: the memberid of the new member
|
||||||
|
|
||||||
|
@ -135,7 +142,7 @@ def new(realname, studentid=None, program=None, mtype='user', userid=None):
|
||||||
# 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 userid == '': userid = None
|
if uid == '': uid = None
|
||||||
if mtype == '': mtype = None
|
if mtype == '': mtype = None
|
||||||
|
|
||||||
# check the student id format
|
# check the student id format
|
||||||
|
@ -147,18 +154,33 @@ def new(realname, studentid=None, program=None, mtype='user', userid=None):
|
||||||
raise InvalidRealName(realname)
|
raise InvalidRealName(realname)
|
||||||
|
|
||||||
# check for duplicate student id
|
# check for duplicate student id
|
||||||
member = connection.select_member_by_studentid(studentid)
|
member = db_connection.select_member_by_studentid(studentid) or \
|
||||||
|
ldap_connection.member_search_studentid(studentid)
|
||||||
if member:
|
if member:
|
||||||
raise DuplicateStudentID(studentid)
|
raise DuplicateStudentID(studentid)
|
||||||
|
|
||||||
# add the member
|
# check for duplicate userid
|
||||||
memberid = connection.insert_member(realname, studentid, program)
|
member = db_connection.select_member_by_userid(uid) or \
|
||||||
|
ldap_connection.user_lookup(uid)
|
||||||
|
if member:
|
||||||
|
raise InvalidArgument("uid", uid, "duplicate uid")
|
||||||
|
|
||||||
# register them for this term
|
# add the member to the database
|
||||||
connection.insert_term(memberid, terms.current())
|
memberid = db_connection.insert_member(realname, studentid, program, userid=uid)
|
||||||
|
|
||||||
# commit the transaction
|
# add the member to the directory
|
||||||
connection.commit()
|
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
|
||||||
|
member = ldap_connection.member_lookup(uid)
|
||||||
|
member['term'] = [ terms.current() ]
|
||||||
|
ldap_connection.user_modify(uid, member)
|
||||||
|
|
||||||
|
# commit the database transaction
|
||||||
|
db_connection.commit()
|
||||||
|
|
||||||
return memberid
|
return memberid
|
||||||
|
|
||||||
|
@ -177,7 +199,7 @@ def get(memberid):
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return connection.select_member_by_id(memberid)
|
return db_connection.select_member_by_id(memberid)
|
||||||
|
|
||||||
|
|
||||||
def get_userid(userid):
|
def get_userid(userid):
|
||||||
|
@ -197,7 +219,7 @@ def get_userid(userid):
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return connection.select_member_by_userid(userid)
|
return db_connection.select_member_by_userid(userid)
|
||||||
|
|
||||||
|
|
||||||
def get_studentid(studentid):
|
def get_studentid(studentid):
|
||||||
|
@ -217,7 +239,7 @@ def get_studentid(studentid):
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return connection.select_member_by_studentid(studentid)
|
return db_connection.select_member_by_studentid(studentid)
|
||||||
|
|
||||||
|
|
||||||
def list_term(term):
|
def list_term(term):
|
||||||
|
@ -237,10 +259,10 @@ def list_term(term):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# retrieve a list of memberids in term
|
# retrieve a list of memberids in term
|
||||||
memberlist = connection.select_members_by_term(term)
|
memberlist = db_connection.select_members_by_term(term)
|
||||||
|
|
||||||
return memberlist.values()
|
return memberlist.values()
|
||||||
|
|
||||||
|
|
||||||
def list_name(name):
|
def list_name(name):
|
||||||
"""
|
"""
|
||||||
|
@ -259,7 +281,7 @@ def list_name(name):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# retrieve a list of memberids matching name
|
# retrieve a list of memberids matching name
|
||||||
memberlist = connection.select_members_by_name(name)
|
memberlist = db_connection.select_members_by_name(name)
|
||||||
|
|
||||||
return memberlist.values()
|
return memberlist.values()
|
||||||
|
|
||||||
|
@ -272,7 +294,7 @@ def list_all():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# retrieve a list of members
|
# retrieve a list of members
|
||||||
memberlist = connection.select_all_members()
|
memberlist = db_connection.select_all_members()
|
||||||
|
|
||||||
return memberlist.values()
|
return memberlist.values()
|
||||||
|
|
||||||
|
@ -292,18 +314,23 @@ def delete(memberid):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# save member data
|
# save member data
|
||||||
member = connection.select_member_by_id(memberid)
|
member = db_connection.select_member_by_id(memberid)
|
||||||
|
|
||||||
# bail if not found
|
# bail if not found
|
||||||
if not member:
|
if not member:
|
||||||
raise NoSuchMember(memberid)
|
raise NoSuchMember(memberid)
|
||||||
|
|
||||||
term_list = connection.select_terms(memberid)
|
term_list = db_connection.select_terms(memberid)
|
||||||
|
|
||||||
# remove data from the db
|
# remove data from the db
|
||||||
connection.delete_term_all(memberid)
|
db_connection.delete_term_all(memberid)
|
||||||
connection.delete_member(memberid)
|
db_connection.delete_member(memberid)
|
||||||
connection.commit()
|
db_connection.commit()
|
||||||
|
|
||||||
|
# remove data from the directory
|
||||||
|
if member and member['userid']:
|
||||||
|
uid = member['userid']
|
||||||
|
ldap_connection.user_delete(uid)
|
||||||
|
|
||||||
return (member, term_list)
|
return (member, term_list)
|
||||||
|
|
||||||
|
@ -334,7 +361,7 @@ def update(member):
|
||||||
raise InvalidStudentID(studentid)
|
raise InvalidStudentID(studentid)
|
||||||
|
|
||||||
# check for duplicate student id
|
# check for duplicate student id
|
||||||
dupmember = connection.select_member_by_studentid(studentid)
|
dupmember = db_connection.select_member_by_studentid(studentid)
|
||||||
if dupmember:
|
if dupmember:
|
||||||
raise DuplicateStudentID(studentid)
|
raise DuplicateStudentID(studentid)
|
||||||
|
|
||||||
|
@ -346,12 +373,12 @@ def update(member):
|
||||||
# see if member exists
|
# see if member exists
|
||||||
if not get(memberid):
|
if not get(memberid):
|
||||||
raise NoSuchMember(memberid)
|
raise NoSuchMember(memberid)
|
||||||
|
|
||||||
# do the update
|
# do the update
|
||||||
connection.update_member(member)
|
db_connection.update_member(member)
|
||||||
|
|
||||||
# commit the transaction
|
# commit the transaction
|
||||||
connection.commit()
|
db_connection.commit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -376,16 +403,31 @@ 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
|
||||||
|
db_member = get(memberid)
|
||||||
|
if db_member['userid']:
|
||||||
|
uid = db_member['userid']
|
||||||
|
ldap_member = ldap_connection.member_lookup(uid)
|
||||||
|
if ldap_member and 'term' not in ldap_member:
|
||||||
|
ldap_member['term'] = []
|
||||||
|
|
||||||
for term in term_list:
|
for term in term_list:
|
||||||
|
|
||||||
# check term syntax
|
# check term syntax
|
||||||
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
|
|
||||||
connection.insert_term(memberid, term)
|
|
||||||
|
|
||||||
connection.commit()
|
# add term to database
|
||||||
|
db_connection.insert_term(memberid, term)
|
||||||
|
|
||||||
|
# add the term to the directory
|
||||||
|
if ldap_member:
|
||||||
|
ldap_member['term'].append(term)
|
||||||
|
|
||||||
|
if ldap_member:
|
||||||
|
ldap_connection.user_modify(uid, ldap_member)
|
||||||
|
|
||||||
|
db_connection.commit()
|
||||||
|
|
||||||
|
|
||||||
def registered(memberid, term):
|
def registered(memberid, term):
|
||||||
|
@ -402,7 +444,7 @@ def registered(memberid, term):
|
||||||
Example: registered(3349, "f2006") -> True
|
Example: registered(3349, "f2006") -> True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return connection.select_term(memberid, term) is not None
|
return db_connection.select_term(memberid, term) is not None
|
||||||
|
|
||||||
|
|
||||||
def member_terms(memberid):
|
def member_terms(memberid):
|
||||||
|
@ -418,7 +460,7 @@ def member_terms(memberid):
|
||||||
Example: registered(0) -> 's1993'
|
Example: registered(0) -> 's1993'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
terms_list = connection.select_terms(memberid)
|
terms_list = db_connection.select_terms(memberid)
|
||||||
terms_list.sort(terms.compare)
|
terms_list.sort(terms.compare)
|
||||||
return terms_list
|
return terms_list
|
||||||
|
|
||||||
|
@ -432,18 +474,19 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
# t=test m=member s=student u=updated
|
# t=test m=member s=student u=updated
|
||||||
tmname = 'Test Member'
|
tmname = 'Test Member'
|
||||||
|
tmuid = 'testmember'
|
||||||
tmprogram = 'Metaphysics'
|
tmprogram = 'Metaphysics'
|
||||||
tmsid = '00000000'
|
tmsid = '00000000'
|
||||||
tm2name = 'Test Member 2'
|
tm2name = 'Test Member 2'
|
||||||
|
tm2uid = 'testmember2'
|
||||||
tm2sid = '00000001'
|
tm2sid = '00000001'
|
||||||
tm2uname = 'Test Member II'
|
tm2uname = 'Test Member II'
|
||||||
tm2usid = '00000002'
|
tm2usid = '00000002'
|
||||||
tm2uprogram = 'Pseudoscience'
|
tm2uprogram = 'Pseudoscience'
|
||||||
tm2uuserid = 'testmember'
|
|
||||||
|
|
||||||
tmdict = {'name': tmname, 'userid': None, 'program': tmprogram, 'type': 'user', 'studentid': tmsid }
|
tmdict = {'name': tmname, 'userid': tmuid, 'program': tmprogram, 'type': 'user', 'studentid': tmsid }
|
||||||
tm2dict = {'name': tm2name, 'userid': None, 'program': None, 'type': 'user', 'studentid': tm2sid }
|
tm2dict = {'name': tm2name, 'userid': tm2uid, 'program': None, 'type': 'user', 'studentid': tm2sid }
|
||||||
tm2udict = {'name': tm2uname, 'userid': tm2uuserid, 'program': tm2uprogram, 'type': 'user', 'studentid': tm2usid }
|
tm2udict = {'name': tm2uname, 'userid': tm2uid, 'program': tm2uprogram, 'type': 'user', 'studentid': tm2usid }
|
||||||
|
|
||||||
thisterm = terms.current()
|
thisterm = terms.current()
|
||||||
nextterm = terms.next(thisterm)
|
nextterm = terms.next(thisterm)
|
||||||
|
@ -464,8 +507,8 @@ if __name__ == '__main__':
|
||||||
if dmid: delete(dmid['memberid'])
|
if dmid: delete(dmid['memberid'])
|
||||||
|
|
||||||
test(new)
|
test(new)
|
||||||
tmid = new(tmname, tmsid, tmprogram)
|
tmid = new(tmuid, tmname, tmsid, tmprogram)
|
||||||
tm2id = new(tm2name, tm2sid)
|
tm2id = new(tm2uid, tm2name, tm2sid)
|
||||||
success()
|
success()
|
||||||
|
|
||||||
tmdict['memberid'] = tmid
|
tmdict['memberid'] = tmid
|
||||||
|
@ -495,7 +538,7 @@ if __name__ == '__main__':
|
||||||
success()
|
success()
|
||||||
|
|
||||||
test(register)
|
test(register)
|
||||||
register(tmid, terms.next(terms.current()))
|
register(tmid, nextterm)
|
||||||
assert_equal(True, registered(tmid, nextterm))
|
assert_equal(True, registered(tmid, nextterm))
|
||||||
success()
|
success()
|
||||||
|
|
||||||
|
@ -517,7 +560,7 @@ if __name__ == '__main__':
|
||||||
success()
|
success()
|
||||||
|
|
||||||
test(get_userid)
|
test(get_userid)
|
||||||
assert_equal(tm2udict, get_userid(tm2uuserid))
|
assert_equal(tm2udict, get_userid(tm2uid))
|
||||||
success()
|
success()
|
||||||
|
|
||||||
test(get_studentid)
|
test(get_studentid)
|
||||||
|
|
|
@ -12,6 +12,7 @@ This frontend is poorly documented, deprecated, and undoubtedly full of bugs.
|
||||||
import curses.ascii, re, os
|
import curses.ascii, re, os
|
||||||
from helpers import menu, inputbox, msgbox, reset
|
from helpers import menu, inputbox, msgbox, reset
|
||||||
from csc.adm import accounts, members, terms
|
from csc.adm import accounts, members, terms
|
||||||
|
from csc.common.excep import InvalidArgument
|
||||||
|
|
||||||
# color of the ceo border
|
# color of the ceo border
|
||||||
BORDER_COLOR = curses.COLOR_RED
|
BORDER_COLOR = curses.COLOR_RED
|
||||||
|
@ -20,7 +21,7 @@ BORDER_COLOR = curses.COLOR_RED
|
||||||
def action_new_member(wnd):
|
def action_new_member(wnd):
|
||||||
"""Interactively add a new member."""
|
"""Interactively add a new member."""
|
||||||
|
|
||||||
studentid, program = None, ''
|
userid, studentid, program = '', None, ''
|
||||||
|
|
||||||
# read the name
|
# read the name
|
||||||
prompt = " Name: "
|
prompt = " Name: "
|
||||||
|
@ -50,12 +51,21 @@ def action_new_member(wnd):
|
||||||
if program is None or program.lower() == 'exit':
|
if program is None or program.lower() == 'exit':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# read user id
|
||||||
|
prompt = "Userid:"
|
||||||
|
while userid == '':
|
||||||
|
userid = inputbox(wnd, prompt, 18)
|
||||||
|
|
||||||
|
# user abort
|
||||||
|
if userid is None or userid.lower() == 'exit':
|
||||||
|
return False
|
||||||
|
|
||||||
# connect the members module to its backend if necessary
|
# connect the members module to its backend if necessary
|
||||||
if not members.connected(): members.connect()
|
if not members.connected(): members.connect()
|
||||||
|
|
||||||
# attempt to create the member
|
# attempt to create the member
|
||||||
try:
|
try:
|
||||||
memberid = members.new(realname, studentid, program)
|
memberid = members.new(userid, realname, studentid, program)
|
||||||
|
|
||||||
msgbox(wnd, "Success! Your memberid is %s. You are now registered\n"
|
msgbox(wnd, "Success! Your memberid is %s. You are now registered\n"
|
||||||
% memberid + "for the " + terms.current() + " term.")
|
% memberid + "for the " + terms.current() + " term.")
|
||||||
|
@ -69,6 +79,12 @@ def action_new_member(wnd):
|
||||||
except members.InvalidRealName:
|
except members.InvalidRealName:
|
||||||
msgbox(wnd, 'Invalid real name: "%s"' % realname)
|
msgbox(wnd, 'Invalid real name: "%s"' % realname)
|
||||||
return False
|
return False
|
||||||
|
except InvalidArgument, e:
|
||||||
|
if e.argname == 'uid' and e.explanation == 'duplicate uid':
|
||||||
|
msgbox(wnd, 'A member with this user ID exists.')
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def action_term_register(wnd):
|
def action_term_register(wnd):
|
||||||
|
@ -196,7 +212,7 @@ def repair_account(wnd, memberid, userid):
|
||||||
password = input_password(wnd)
|
password = input_password(wnd)
|
||||||
accounts.create_member(userid, password, member['name'], memberid)
|
accounts.create_member(userid, password, member['name'], memberid)
|
||||||
msgbox(wnd, "Account created (where the hell did it go, anyway?)\n"
|
msgbox(wnd, "Account created (where the hell did it go, anyway?)\n"
|
||||||
"If you're homedir still exists, it will not be inaccessible to you,\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")
|
"please contact systems-committee@csclub.uwaterloo.ca to get this resolved.\n")
|
||||||
|
|
||||||
elif not haspw:
|
elif not haspw:
|
||||||
|
@ -267,7 +283,8 @@ def action_create_account(wnd):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# member already has an account?
|
# member already has an account?
|
||||||
if member['userid']:
|
if not accounts.connected(): accounts.connect()
|
||||||
|
if member['userid'] and accounts.status(member['userid'])[0]:
|
||||||
|
|
||||||
userid = member['userid']
|
userid = member['userid']
|
||||||
msgbox(wnd, "Member " + str(memberid) + " already has an account: " + member['userid'] + "\n"
|
msgbox(wnd, "Member " + str(memberid) + " already has an account: " + member['userid'] + "\n"
|
||||||
|
@ -278,6 +295,9 @@ def action_create_account(wnd):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
if member['userid']:
|
||||||
|
userid = member['userid']
|
||||||
|
|
||||||
# read user id
|
# read user id
|
||||||
prompt = "Userid:"
|
prompt = "Userid:"
|
||||||
while userid == '':
|
while userid == '':
|
||||||
|
|
|
@ -98,7 +98,7 @@ class LDAPConnection(object):
|
||||||
|
|
||||||
### Helper Methods ###
|
### Helper Methods ###
|
||||||
|
|
||||||
def lookup(self, dn):
|
def lookup(self, dn, objectClass=None):
|
||||||
"""
|
"""
|
||||||
Helper method to retrieve the attributes of an entry.
|
Helper method to retrieve the attributes of an entry.
|
||||||
|
|
||||||
|
@ -113,7 +113,11 @@ class LDAPConnection(object):
|
||||||
|
|
||||||
# search for the specified dn
|
# search for the specified dn
|
||||||
try:
|
try:
|
||||||
matches = self.ldap.search_s(dn, ldap.SCOPE_BASE)
|
if objectClass:
|
||||||
|
search_filter = '(objectClass=%s)' % self.escape(objectClass)
|
||||||
|
matches = self.ldap.search_s(dn, ldap.SCOPE_BASE, search_filter)
|
||||||
|
else:
|
||||||
|
matches = self.ldap.search_s(dn, ldap.SCOPE_BASE)
|
||||||
except ldap.NO_SUCH_OBJECT:
|
except ldap.NO_SUCH_OBJECT:
|
||||||
return None
|
return None
|
||||||
except ldap.LDAPError, e:
|
except ldap.LDAPError, e:
|
||||||
|
@ -122,109 +126,160 @@ class LDAPConnection(object):
|
||||||
# this should never happen due to the nature of DNs
|
# this should never happen due to the nature of DNs
|
||||||
if len(matches) > 1:
|
if len(matches) > 1:
|
||||||
raise LDAPException("duplicate dn in ldap: " + dn)
|
raise LDAPException("duplicate dn in ldap: " + dn)
|
||||||
|
|
||||||
|
# dn was found, but didn't match the objectClass filter
|
||||||
|
elif len(matches) < 1:
|
||||||
|
return None
|
||||||
|
|
||||||
# return the attributes of the single successful match
|
# return the attributes of the single successful match
|
||||||
else:
|
match = matches[0]
|
||||||
match = matches[0]
|
match_dn, match_attributes = match
|
||||||
match_dn, match_attributes = match
|
return match_attributes
|
||||||
return match_attributes
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### User-related Methods ###
|
### User-related Methods ###
|
||||||
|
|
||||||
def user_lookup(self, uid):
|
def user_lookup(self, uid, objectClass=None):
|
||||||
"""
|
"""
|
||||||
Retrieve the attributes of a user.
|
Retrieve the attributes of a user.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
uid - the UNIX username to look up
|
uid - the uid to look up
|
||||||
|
|
||||||
Returns: attributes of user with uid
|
Returns: attributes of user with uid
|
||||||
|
|
||||||
Example: connection.user_lookup('mspang') ->
|
|
||||||
{ 'uid': 'mspang', 'uidNumber': 21292 ...}
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
dn = 'uid=' + uid + ',' + self.user_base
|
dn = 'uid=' + uid + ',' + self.user_base
|
||||||
return self.lookup(dn)
|
return self.lookup(dn, objectClass)
|
||||||
|
|
||||||
|
|
||||||
def user_search(self, search_filter):
|
def user_search(self, search_filter, params):
|
||||||
"""
|
"""
|
||||||
Helper for user searches.
|
Search for users with a filter.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
search_filter - LDAP filter string to match users against
|
search_filter - LDAP filter string to match users against
|
||||||
|
|
||||||
Returns: the list of uids matched (usernames)
|
Returns: a dictionary mapping uids to attributes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not self.connected(): raise LDAPException("Not connected!")
|
if not self.connected(): raise LDAPException("Not connected!")
|
||||||
|
|
||||||
|
search_filter = search_filter % tuple(self.escape(x) for x in params)
|
||||||
|
|
||||||
# search for entries that match the filter
|
# search for entries that match the filter
|
||||||
try:
|
try:
|
||||||
matches = self.ldap.search_s(self.user_base, ldap.SCOPE_SUBTREE, search_filter)
|
matches = self.ldap.search_s(self.user_base, ldap.SCOPE_SUBTREE, search_filter)
|
||||||
except ldap.LDAPError, e:
|
except ldap.LDAPError, e:
|
||||||
raise LDAPException("user search failed: %s" % e)
|
raise LDAPException("user search failed: %s" % e)
|
||||||
|
|
||||||
# list for uids found
|
results = {}
|
||||||
uids = []
|
|
||||||
|
|
||||||
for match in matches:
|
for match in matches:
|
||||||
dn, attributes = match
|
dn, attrs = match
|
||||||
|
uid = attrs['uid'][0]
|
||||||
# uid is a required attribute of posixAccount
|
results[uid] = attrs
|
||||||
if not attributes.has_key('uid'):
|
|
||||||
raise LDAPException(dn + ' (posixAccount) has no uid')
|
|
||||||
|
|
||||||
# do not handle the case of multiple usernames in one entry (yet)
|
|
||||||
elif len(attributes['uid']) > 1:
|
|
||||||
raise LDAPException(dn + ' (posixAccount) has multiple uids')
|
|
||||||
|
|
||||||
# append the sole uid of this match to the list
|
|
||||||
uids.append( attributes['uid'][0] )
|
|
||||||
|
|
||||||
return uids
|
return results
|
||||||
|
|
||||||
|
|
||||||
def user_search_id(self, uidNumber):
|
def user_modify(self, uid, attrs):
|
||||||
"""
|
"""
|
||||||
Retrieves a list of users with a certain UNIX uid number.
|
Update user attributes in the directory.
|
||||||
|
|
||||||
LDAP (or passwd for that matter) does not enforce any
|
Parameters:
|
||||||
restriction on the number of accounts that can have
|
uid - username of the user to modify
|
||||||
a certain UID. Therefore this method returns a list of matches.
|
attrs - dictionary as returned by user_lookup() with changes to make.
|
||||||
|
omitted attributes are DELETED.
|
||||||
|
|
||||||
|
Example: user = user_lookup('mspang')
|
||||||
|
user['uidNumber'] = [ '0' ]
|
||||||
|
connection.user_modify('mspang', user)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# distinguished name of the entry to modify
|
||||||
|
dn = 'uid=' + uid + ',' + self.user_base
|
||||||
|
|
||||||
|
# retrieve current state of user
|
||||||
|
old_user = self.user_lookup(uid)
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# build list of modifications to make
|
||||||
|
changes = ldap.modlist.modifyModlist(old_user, attrs)
|
||||||
|
|
||||||
|
# apply changes
|
||||||
|
self.ldap.modify_s(dn, changes)
|
||||||
|
|
||||||
|
except ldap.LDAPError, e:
|
||||||
|
raise LDAPException("unable to modify: %s" % e)
|
||||||
|
|
||||||
|
|
||||||
|
def user_delete(self, uid):
|
||||||
|
"""
|
||||||
|
Removes a user from the directory.
|
||||||
|
|
||||||
|
Example: connection.user_delete('mspang')
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
dn = 'uid=' + uid + ',' + self.user_base
|
||||||
|
self.ldap.delete_s(dn)
|
||||||
|
except ldap.LDAPError, e:
|
||||||
|
raise LDAPException("unable to delete: %s" % e)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Account-related Methods ###
|
||||||
|
|
||||||
|
def account_lookup(self, uid):
|
||||||
|
"""
|
||||||
|
Retrieve the attributes of an account.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
uid - the uid to look up
|
||||||
|
|
||||||
|
Returns: attributes of user with uid
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.user_lookup(uid, 'posixAccount')
|
||||||
|
|
||||||
|
|
||||||
|
def account_search_id(self, uidNumber):
|
||||||
|
"""
|
||||||
|
Retrieves a list of accounts with a certain UNIX uid number.
|
||||||
|
|
||||||
|
LDAP (or passwd for that matter) does not enforce any restriction on
|
||||||
|
the number of accounts that can have a certain UID number. Therefore
|
||||||
|
this method returns a list of matches.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
uidNumber - the user id of the accounts desired
|
uidNumber - the user id of the accounts desired
|
||||||
|
|
||||||
Returns: the list of uids matched (usernames)
|
Returns: a dictionary mapping uids to attributes
|
||||||
|
|
||||||
Example: connection.user_search_id(21292) -> ['mspang']
|
Example: connection.account_search_id(21292) -> {'mspang': { ... }}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# search for posixAccount entries with the specified uidNumber
|
search_filter = '(&(objectClass=posixAccount)(uidNumber=%s))'
|
||||||
search_filter = '(&(objectClass=posixAccount)(uidNumber=%d))' % uidNumber
|
return self.user_search(search_filter, [ uidNumber ])
|
||||||
return self.user_search(search_filter)
|
|
||||||
|
|
||||||
|
|
||||||
def user_search_gid(self, gidNumber):
|
def account_search_gid(self, gidNumber):
|
||||||
"""
|
"""
|
||||||
Retrieves a list of users with a certain UNIX gid
|
Retrieves a list of accounts with a certain UNIX gid
|
||||||
number (search by default group).
|
number (search by default group).
|
||||||
|
|
||||||
Returns: the list of uids matched (usernames)
|
Returns: a dictionary mapping uids to attributes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# search for posixAccount entries with the specified gidNumber
|
search_filter = '(&(objectClass=posixAccount)(gidNumber=%s))'
|
||||||
search_filter = '(&(objectClass=posixAccount)(gidNumber=%d))' % gidNumber
|
return self.user_search(search_filter, [ gidNumber ])
|
||||||
return self.user_search(search_filter)
|
|
||||||
|
|
||||||
|
|
||||||
def user_add(self, uid, cn, uidNumber, gidNumber, homeDirectory, loginShell=None, gecos=None, description=None):
|
def account_add(self, uid, cn, uidNumber, gidNumber, homeDirectory, loginShell=None, gecos=None, description=None, update=False):
|
||||||
"""
|
"""
|
||||||
Adds a user to the directory.
|
Adds a user account to the directory.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
uid - the UNIX username for the account
|
uid - the UNIX username for the account
|
||||||
|
@ -235,6 +290,7 @@ class LDAPConnection(object):
|
||||||
loginShell - login shell for the user
|
loginShell - login shell for the user
|
||||||
gecos - comment field (usually stores name etc)
|
gecos - comment field (usually stores name etc)
|
||||||
description - description field (optional and unimportant)
|
description - description field (optional and unimportant)
|
||||||
|
update - if True, will update existing entries
|
||||||
|
|
||||||
Example: connection.user_add('mspang', 'Michael Spang',
|
Example: connection.user_add('mspang', 'Michael Spang',
|
||||||
21292, 100, '/users/mspang', '/bin/bash',
|
21292, 100, '/users/mspang', '/bin/bash',
|
||||||
|
@ -254,69 +310,35 @@ class LDAPConnection(object):
|
||||||
'homeDirectory': [ homeDirectory ],
|
'homeDirectory': [ homeDirectory ],
|
||||||
'gecos': [ gecos ],
|
'gecos': [ gecos ],
|
||||||
}
|
}
|
||||||
|
|
||||||
if loginShell:
|
if loginShell:
|
||||||
attrs['loginShell'] = loginShell
|
attrs['loginShell'] = [ loginShell ]
|
||||||
if description:
|
if description:
|
||||||
attrs['description'] = [ description ]
|
attrs['description'] = [ description ]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
modlist = ldap.modlist.addModlist(attrs)
|
|
||||||
self.ldap.add_s(dn, modlist)
|
old_entry = self.user_lookup(uid)
|
||||||
|
if old_entry and 'posixAccount' not in old_entry['objectClass'] and update:
|
||||||
|
|
||||||
|
attrs.update(old_entry)
|
||||||
|
attrs['objectClass'] = list(attrs['objectClass'])
|
||||||
|
attrs['objectClass'].append('posixAccount')
|
||||||
|
if not 'shadowAccount' in attrs['objectClass']:
|
||||||
|
attrs['objectClass'].append('shadowAccount')
|
||||||
|
|
||||||
|
modlist = ldap.modlist.modifyModlist(old_entry, attrs)
|
||||||
|
self.ldap.modify_s(dn, modlist)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
modlist = ldap.modlist.addModlist(attrs)
|
||||||
|
self.ldap.add_s(dn, modlist)
|
||||||
|
|
||||||
except ldap.LDAPError, e:
|
except ldap.LDAPError, e:
|
||||||
raise LDAPException("unable to add: %s" % e)
|
raise LDAPException("unable to add: %s" % e)
|
||||||
|
|
||||||
|
|
||||||
def user_modify(self, uid, attrs):
|
|
||||||
"""
|
|
||||||
Update user attributes in the directory.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
uid - username of the user to modify
|
|
||||||
attrs - dictionary as returned by user_lookup() with changes to make.
|
|
||||||
omitted attributes are DELETED.
|
|
||||||
|
|
||||||
Example: user = user_lookup('mspang')
|
|
||||||
user['uidNumber'] = [ '0' ]
|
|
||||||
connection.user_modify('mspang', user)
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not self.connected(): raise LDAPException("Not connected!")
|
|
||||||
|
|
||||||
# distinguished name of the entry to modify
|
|
||||||
dn = 'uid=' + uid + ',' + self.user_base
|
|
||||||
|
|
||||||
# retrieve current state of user
|
|
||||||
old_user = self.user_lookup(uid)
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
# build list of modifications to make
|
|
||||||
changes = ldap.modlist.modifyModlist(old_user, attrs)
|
|
||||||
|
|
||||||
# apply changes
|
|
||||||
self.ldap.modify_s(dn, changes)
|
|
||||||
|
|
||||||
except ldap.LDAPError, e:
|
|
||||||
raise LDAPException("unable to modify: %s" % e)
|
|
||||||
|
|
||||||
|
|
||||||
def user_delete(self, uid):
|
|
||||||
"""
|
|
||||||
Removes a user from the directory.
|
|
||||||
|
|
||||||
Example: connection.user_delete('mspang')
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not self.connected(): raise LDAPException("Not connected!")
|
|
||||||
|
|
||||||
try:
|
|
||||||
dn = 'uid=' + uid + ',' + self.user_base
|
|
||||||
self.ldap.delete_s(dn)
|
|
||||||
except ldap.LDAPError, e:
|
|
||||||
raise LDAPException("unable to delete: %s" % e)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Group-related Methods ###
|
### Group-related Methods ###
|
||||||
|
|
||||||
|
@ -355,27 +377,19 @@ class LDAPConnection(object):
|
||||||
try:
|
try:
|
||||||
search_filter = '(&(objectClass=posixGroup)(gidNumber=%d))' % gidNumber
|
search_filter = '(&(objectClass=posixGroup)(gidNumber=%d))' % gidNumber
|
||||||
matches = self.ldap.search_s(self.group_base, ldap.SCOPE_SUBTREE, search_filter)
|
matches = self.ldap.search_s(self.group_base, ldap.SCOPE_SUBTREE, search_filter)
|
||||||
except ldap.LDAPError,e :
|
except ldap.LDAPError, e:
|
||||||
raise LDAPException("group search failed: %s" % e)
|
raise LDAPException("group search failed: %s" % e)
|
||||||
|
|
||||||
# list for groups found
|
# list for groups found
|
||||||
group_cns = []
|
group_cns = []
|
||||||
|
|
||||||
|
results = {}
|
||||||
for match in matches:
|
for match in matches:
|
||||||
dn, attributes = match
|
dn, attrs = match
|
||||||
|
uid = attrs['cn'][0]
|
||||||
|
results[uid] = attrs
|
||||||
|
|
||||||
# cn is a required attribute of posixGroup
|
return results
|
||||||
if not attributes.has_key('cn'):
|
|
||||||
raise LDAPException(dn + ' (posixGroup) has no cn')
|
|
||||||
|
|
||||||
# do not handle the case of multiple cns for one group (yet)
|
|
||||||
elif len(attributes['cn']) > 1:
|
|
||||||
raise LDAPException(dn + ' (posixGroup) has multiple cns')
|
|
||||||
|
|
||||||
# append the sole uid of this match to the list
|
|
||||||
group_cns.append( attributes['cn'][0] )
|
|
||||||
|
|
||||||
return group_cns
|
|
||||||
|
|
||||||
|
|
||||||
def group_add(self, cn, gidNumber, description=None):
|
def group_add(self, cn, gidNumber, description=None):
|
||||||
|
@ -457,8 +471,129 @@ class LDAPConnection(object):
|
||||||
raise LDAPException("unable to delete group: %s" % e)
|
raise LDAPException("unable to delete group: %s" % e)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Member-related Methods ###
|
||||||
|
|
||||||
|
def member_lookup(self, uid):
|
||||||
|
"""
|
||||||
|
Retrieve the attributes of a member. This method will only return
|
||||||
|
results that have the objectClass 'member'.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
uid - the username to look up
|
||||||
|
|
||||||
|
Returns: attributes of member with uid
|
||||||
|
|
||||||
|
Example: connection.member_lookup('mspang') ->
|
||||||
|
{ 'uid': 'mspang', 'uidNumber': 21292 ...}
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.connected(): raise LDAPException("Not connected!")
|
||||||
|
|
||||||
|
dn = 'uid=' + uid + ',' + self.user_base
|
||||||
|
return self.lookup(dn, 'member')
|
||||||
|
|
||||||
|
|
||||||
|
def member_search_studentid(self, studentid):
|
||||||
|
"""
|
||||||
|
Retrieves a list of members with a certain studentid.
|
||||||
|
|
||||||
|
Returns: a dictionary mapping uids to attributes
|
||||||
|
"""
|
||||||
|
|
||||||
|
search_filter = '(&(objectClass=member)(studentid=%s))'
|
||||||
|
return self.user_search(search_filter, [ studentid ] )
|
||||||
|
|
||||||
|
|
||||||
|
def member_search_name(self, name):
|
||||||
|
"""
|
||||||
|
Retrieves a list of members with the specified name (fuzzy).
|
||||||
|
|
||||||
|
Returns: a dictionary mapping uids to attributes
|
||||||
|
"""
|
||||||
|
|
||||||
|
search_filter = '(&(objectClass=member)(cn~=%s))'
|
||||||
|
return self.user_search(search_filter, [ name ] )
|
||||||
|
|
||||||
|
|
||||||
|
def member_search_term(self, term):
|
||||||
|
"""
|
||||||
|
Retrieves a list of members who were registered in a certain term.
|
||||||
|
|
||||||
|
Returns: a dictionary mapping uids to attributes
|
||||||
|
"""
|
||||||
|
|
||||||
|
search_filter = '(&(objectClass=member)(term=%s))'
|
||||||
|
return self.user_search(search_filter, [ term ])
|
||||||
|
|
||||||
|
|
||||||
|
def member_search_program(self, program):
|
||||||
|
"""
|
||||||
|
Retrieves a list of members in a certain program (fuzzy).
|
||||||
|
|
||||||
|
Returns: a dictionary mapping uids to attributes
|
||||||
|
"""
|
||||||
|
|
||||||
|
search_filter = '(&(objectClass=member)(program~=%s))'
|
||||||
|
return self.user_search(search_filter, [ program ])
|
||||||
|
|
||||||
|
|
||||||
|
def member_add(self, uid, cn, studentid, program=None, description=None):
|
||||||
|
"""
|
||||||
|
Adds a member to the directory.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
uid - the UNIX username for the member
|
||||||
|
cn - the real name of the member
|
||||||
|
studentid - the member's student ID number
|
||||||
|
program - the member's program of study
|
||||||
|
description - a description for the entry
|
||||||
|
"""
|
||||||
|
|
||||||
|
dn = 'uid=' + uid + ',' + self.user_base
|
||||||
|
attrs = {
|
||||||
|
'objectClass': [ 'top', 'account', 'member' ],
|
||||||
|
'uid': [ uid ],
|
||||||
|
'cn': [ cn ],
|
||||||
|
'studentid': [ studentid ],
|
||||||
|
}
|
||||||
|
|
||||||
|
if program:
|
||||||
|
attrs['program'] = [ program ]
|
||||||
|
if description:
|
||||||
|
attrs['description'] = [ description ]
|
||||||
|
|
||||||
|
try:
|
||||||
|
modlist = ldap.modlist.addModlist(attrs)
|
||||||
|
self.ldap.add_s(dn, modlist)
|
||||||
|
except ldap.LDAPError, e:
|
||||||
|
raise LDAPException("unable to add: %s" % e)
|
||||||
|
|
||||||
|
|
||||||
|
def member_add_account(self, uid, uidNumber, gidNumber, homeDirectory, loginShell=None, gecos=None):
|
||||||
|
"""
|
||||||
|
Adds login privileges to a member.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.account_add(uid, None, uidNumber, gidNumber, homeDirectory, loginShell, gecos, None, True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Miscellaneous Methods ###
|
### Miscellaneous Methods ###
|
||||||
|
|
||||||
|
def escape(self, value):
|
||||||
|
"""
|
||||||
|
Escapes special characters in a value so that it may be safely inserted
|
||||||
|
into an LDAP search filter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
value = str(value)
|
||||||
|
value = value.replace('\\', '\\5c').replace('*', '\\2a')
|
||||||
|
value = value.replace('(', '\\28').replace(')', '\\29')
|
||||||
|
value = value.replace('\x00', '\\00')
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def used_uids(self, minimum=None, maximum=None):
|
def used_uids(self, minimum=None, maximum=None):
|
||||||
"""
|
"""
|
||||||
Compiles a list of used UIDs in a range.
|
Compiles a list of used UIDs in a range.
|
||||||
|
@ -543,9 +678,17 @@ if __name__ == '__main__':
|
||||||
tushell = '/bin/false'
|
tushell = '/bin/false'
|
||||||
tugecos = 'Test User,,,'
|
tugecos = 'Test User,,,'
|
||||||
tgname = 'testgroup'
|
tgname = 'testgroup'
|
||||||
|
tmname = 'testmember'
|
||||||
|
tmrname = 'Test Member'
|
||||||
|
tmstudentid = '99999999'
|
||||||
|
tmprogram = 'UBW'
|
||||||
|
tmdesc = 'Test Description'
|
||||||
cushell = '/bin/true'
|
cushell = '/bin/true'
|
||||||
cuhome = '/home/changed'
|
cuhome = '/home/changed'
|
||||||
curname = 'Test Modified User'
|
curname = 'Test Modified User'
|
||||||
|
cmhome = '/home/testmember'
|
||||||
|
cmshell = '/bin/false'
|
||||||
|
cmgecos = 'Test Member,,,'
|
||||||
|
|
||||||
test(LDAPConnection)
|
test(LDAPConnection)
|
||||||
connection = LDAPConnection()
|
connection = LDAPConnection()
|
||||||
|
@ -563,6 +706,7 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
try:
|
try:
|
||||||
connection.user_delete(tuname)
|
connection.user_delete(tuname)
|
||||||
|
connection.user_delete(tmname)
|
||||||
connection.group_delete(tgname)
|
connection.group_delete(tgname)
|
||||||
except LDAPException:
|
except LDAPException:
|
||||||
pass
|
pass
|
||||||
|
@ -596,8 +740,20 @@ if __name__ == '__main__':
|
||||||
'cn': [ turname ]
|
'cn': [ turname ]
|
||||||
}
|
}
|
||||||
|
|
||||||
test(LDAPConnection.user_add)
|
test(LDAPConnection.account_add)
|
||||||
connection.user_add(tuname, turname, tuuid, tugid, tuhome, tushell, tugecos)
|
connection.account_add(tuname, turname, tuuid, tugid, tuhome, tushell, tugecos)
|
||||||
|
success()
|
||||||
|
|
||||||
|
emdata = {
|
||||||
|
'uid': [ tmname ],
|
||||||
|
'cn': [ tmrname ],
|
||||||
|
'studentid': [ tmstudentid ],
|
||||||
|
'program': [ tmprogram ],
|
||||||
|
'description': [ tmdesc ],
|
||||||
|
}
|
||||||
|
|
||||||
|
test(LDAPConnection.member_add)
|
||||||
|
connection.member_add(tmname, tmrname, tmstudentid, tmprogram, tmdesc)
|
||||||
success()
|
success()
|
||||||
|
|
||||||
tggid = unusedids.pop()
|
tggid = unusedids.pop()
|
||||||
|
@ -610,41 +766,95 @@ if __name__ == '__main__':
|
||||||
connection.group_add(tgname, tggid)
|
connection.group_add(tgname, tggid)
|
||||||
success()
|
success()
|
||||||
|
|
||||||
|
test(LDAPConnection.account_lookup)
|
||||||
|
udata = connection.account_lookup(tuname)
|
||||||
|
if udata: del udata['objectClass']
|
||||||
|
assert_equal(eudata, udata)
|
||||||
|
success()
|
||||||
|
|
||||||
|
test(LDAPConnection.member_lookup)
|
||||||
|
mdata = connection.member_lookup(tmname)
|
||||||
|
if mdata: del mdata['objectClass']
|
||||||
|
assert_equal(emdata, mdata)
|
||||||
|
success()
|
||||||
|
|
||||||
test(LDAPConnection.user_lookup)
|
test(LDAPConnection.user_lookup)
|
||||||
udata = connection.user_lookup(tuname)
|
udata = connection.user_lookup(tuname)
|
||||||
del udata['objectClass']
|
mdata = connection.user_lookup(tmname)
|
||||||
|
if udata: del udata['objectClass']
|
||||||
|
if mdata: del mdata['objectClass']
|
||||||
assert_equal(eudata, udata)
|
assert_equal(eudata, udata)
|
||||||
|
assert_equal(emdata, mdata)
|
||||||
success()
|
success()
|
||||||
|
|
||||||
test(LDAPConnection.group_lookup)
|
test(LDAPConnection.group_lookup)
|
||||||
gdata = connection.group_lookup(tgname)
|
gdata = connection.group_lookup(tgname)
|
||||||
del gdata['objectClass']
|
if gdata: del gdata['objectClass']
|
||||||
assert_equal(egdata, gdata)
|
assert_equal(egdata, gdata)
|
||||||
success()
|
success()
|
||||||
|
|
||||||
test(LDAPConnection.user_search_id)
|
test(LDAPConnection.account_search_id)
|
||||||
eulist = [ tuname ]
|
eulist = [ tuname ]
|
||||||
ulist = connection.user_search_id(tuuid)
|
ulist = connection.account_search_id(tuuid).keys()
|
||||||
assert_equal(eulist, ulist)
|
assert_equal(eulist, ulist)
|
||||||
success()
|
success()
|
||||||
|
|
||||||
test(LDAPConnection.user_search_gid)
|
test(LDAPConnection.account_search_gid)
|
||||||
ulist = connection.user_search_gid(tugid)
|
ulist = connection.account_search_gid(tugid)
|
||||||
if tuname not in ulist:
|
if tuname not in ulist:
|
||||||
fail("(%s) not in (%s)" % (tuname, ulist))
|
fail("%s not in %s" % (tuname, ulist))
|
||||||
success()
|
success()
|
||||||
|
|
||||||
ecudata = connection.user_lookup(tuname)
|
test(LDAPConnection.member_search_studentid)
|
||||||
|
mlist = connection.member_search_studentid(tmstudentid).keys()
|
||||||
|
emlist = [ tmname ]
|
||||||
|
assert_equal(emlist, mlist)
|
||||||
|
success()
|
||||||
|
|
||||||
|
test(LDAPConnection.member_search_name)
|
||||||
|
mlist = connection.member_search_name(tmrname)
|
||||||
|
if tmname not in mlist:
|
||||||
|
fail("%s not in %s" % (tmname, mlist))
|
||||||
|
success()
|
||||||
|
|
||||||
|
test(LDAPConnection.member_search_program)
|
||||||
|
mlist = connection.member_search_program(tmprogram)
|
||||||
|
if tmname not in mlist:
|
||||||
|
fail("%s not in %s" % (tmname, mlist))
|
||||||
|
success()
|
||||||
|
|
||||||
|
test(LDAPConnection.group_search_id)
|
||||||
|
glist = connection.group_search_id(tggid).keys()
|
||||||
|
eglist = [ tgname ]
|
||||||
|
assert_equal(eglist, glist)
|
||||||
|
success()
|
||||||
|
|
||||||
|
ecudata = connection.account_lookup(tuname)
|
||||||
ecudata['loginShell'] = [ cushell ]
|
ecudata['loginShell'] = [ cushell ]
|
||||||
ecudata['homeDirectory'] = [ cuhome ]
|
ecudata['homeDirectory'] = [ cuhome ]
|
||||||
ecudata['cn'] = [ curname ]
|
ecudata['cn'] = [ curname ]
|
||||||
|
|
||||||
test(LDAPConnection.user_modify)
|
test(LDAPConnection.user_modify)
|
||||||
connection.user_modify(tuname, ecudata)
|
connection.user_modify(tuname, ecudata)
|
||||||
cudata = connection.user_lookup(tuname)
|
cudata = connection.account_lookup(tuname)
|
||||||
assert_equal(ecudata, cudata)
|
assert_equal(ecudata, cudata)
|
||||||
success()
|
success()
|
||||||
|
|
||||||
|
tmuid = unusedids.pop()
|
||||||
|
tmgid = unusedids.pop()
|
||||||
|
emadata = emdata.copy()
|
||||||
|
emadata.update({
|
||||||
|
'loginShell': [ cmshell ],
|
||||||
|
'uidNumber': [ str(tmuid) ],
|
||||||
|
'gidNumber': [ str(tmgid) ],
|
||||||
|
'gecos': [ cmgecos ],
|
||||||
|
'homeDirectory': [ cmhome ],
|
||||||
|
})
|
||||||
|
|
||||||
|
test(LDAPConnection.member_add_account)
|
||||||
|
connection.member_add_account(tmname, tmuid, tmuid, cmhome, cmshell, cmgecos)
|
||||||
|
success()
|
||||||
|
|
||||||
ecgdata = connection.group_lookup(tgname)
|
ecgdata = connection.group_lookup(tgname)
|
||||||
ecgdata['memberUid'] = [ tuname ]
|
ecgdata['memberUid'] = [ tuname ]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue