Merge accounts and members

This really only moves two functions: create_member and
create_club. Nothing else is left in accounts. Eventually,
it might make sense to separate it out again in a more
sane way (e.g. they should share the connection somehow).
This commit is contained in:
Michael Spang 2007-12-12 00:56:57 -05:00
parent 8782e58118
commit 5bca2288af
11 changed files with 108 additions and 189 deletions

View File

@ -1,8 +0,0 @@
# /etc/csc/members.cf: CSC Members Configuration
include /etc/csc/ldap.cf
### Validation Tuning ###
studentid_regex = "^[0-9]{8}$"
realname_regex = "^[^,:=]*$"

View File

@ -4,6 +4,5 @@ CSC Administrative Modules
This module provides member and account management modules.
members - member registration management functions
accounts - account administration functions
terms - helper routines for manipulating terms
"""

View File

@ -1,152 +0,0 @@
"""
UNIX Accounts Administration
This module contains functions for creating, deleting, and manipulating
UNIX user accounts and account groups in the CSC LDAP directory.
"""
import re, pwd, grp, os, subprocess
from csc.common import conf
from csc.common.excep import InvalidArgument
from csc.backends import ldapi
### Configuration ###
CONFIG_FILE = '/etc/csc/accounts.cf'
cfg = {}
def configure():
"""Helper to load the accounts configuration. You need not call this."""
string_fields = [ 'member_shell', 'member_home', 'member_desc',
'member_group', 'club_shell', 'club_home', 'club_desc',
'club_group', 'admin_shell', 'admin_home', 'admin_desc',
'admin_group', 'group_desc', 'username_regex', 'groupname_regex',
'shells_file', 'server_url', 'users_base', 'groups_base',
'sasl_mech', 'sasl_realm', 'admin_bind_keytab',
'admin_bind_userid', 'realm', 'admin_principal', 'admin_keytab' ]
numeric_fields = [ 'member_min_id', 'member_max_id', 'club_min_id',
'club_max_id', 'admin_min_id', 'admin_max_id', 'group_min_id',
'group_max_id', 'min_password_length' ]
# read configuration file
cfg_tmp = conf.read(CONFIG_FILE)
# verify configuration (not necessary, but prints a useful error)
conf.check_string_fields(CONFIG_FILE, string_fields, cfg_tmp)
conf.check_integer_fields(CONFIG_FILE, numeric_fields, cfg_tmp)
# update the current configuration with the loaded values
cfg.update(cfg_tmp)
### Exceptions ###
LDAPException = ldapi.LDAPException
ConfigurationException = conf.ConfigurationException
class AccountException(Exception):
"""Base exception class for account-related errors."""
class ChildFailed(AccountException):
def __init__(self, program, status, output):
self.program, self.status, self.output = program, status, output
def __str__(self):
msg = '%s failed with status %d' % (self.program, self.status)
if self.output:
msg += ': %s' % self.output
return msg
### Connection Management ###
ldap_connection = ldapi.LDAPConnection()
def connect():
"""Connect to LDAP and Kerberos and load configuration. You must call before anything else."""
configure()
# connect to the LDAP server
ldap_connection.connect_sasl(cfg['server_url'], cfg['sasl_mech'],
cfg['sasl_realm'], cfg['admin_bind_userid'],
('keytab', cfg['admin_bind_keytab']), cfg['users_base'],
cfg['groups_base'])
def disconnect():
"""Disconnect from LDAP and Kerberos. Call this before quitting."""
ldap_connection.disconnect()
### Account Types ###
def create_member(username, password, name, program):
"""
Creates a UNIX user account with options tailored to CSC members.
Parameters:
username - the desired UNIX username
password - the desired UNIX password
name - the member's real name
program - the member's program of study
Exceptions:
InvalidArgument - on bad account attributes provided
Returns: the uid number of the new account
See: create()
"""
# check connection
if not connected():
raise AccountException("not connected to LDAP and Kerberos")
# check username format
if not username or not re.match(cfg['username_regex'], username):
raise InvalidArgument("username", username, "expected format %s" % repr(cfg['username_regex']))
# check password length
if not password or len(password) < cfg['min_password_length']:
raise InvalidArgument("password", "<hidden>", "too short (minimum %d characters)" % cfg['min_password_length'])
args = [ "/usr/bin/addmember", "--stdin", username, name, program ]
addmember = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = addmember.communicate(password)
status = addmember.wait()
if status:
raise ChildFailed("addmember", status, out+err)
def create_club(username, name):
"""
Creates a UNIX user account with options tailored to CSC-hosted clubs.
Parameters:
username - the desired UNIX username
name - the club name
Exceptions:
InvalidArgument - on bad account attributes provided
Returns: the uid number of the new account
See: create()
"""
# check username format
if not username or not re.match(cfg['username_regex'], username):
raise InvalidArgument("username", username, "expected format %s" % repr(cfg['username_regex']))
args = [ "/usr/bin/addclub", username, name ]
addclub = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = addclub.communicate()
status = addclub.wait()
if status:
raise ChildFailed("addclub", status, out+err)

View File

@ -9,31 +9,38 @@ Transactions are used in each method that modifies the database.
Future changes to the members database that need to be atomic
must also be moved into this module.
"""
import re, ldap
from csc.adm import terms
from csc.backends import ldapi
import re, subprocess, ldap
from csc.common import conf
from csc.common.excep import InvalidArgument
from csc.backends import ldapi
### Configuration ###
CONFIG_FILE = '/etc/csc/members.cf'
CONFIG_FILE = '/etc/csc/accounts.cf'
cfg = {}
def load_configuration():
def configure():
"""Load Members Configuration"""
string_fields = [ 'realname_regex', 'server_url', 'users_base',
'groups_base', 'sasl_mech', 'sasl_realm', 'admin_bind_keytab',
'admin_bind_userid' ]
string_fields = [ 'member_shell', 'member_home', 'member_desc',
'member_group', 'club_shell', 'club_home', 'club_desc',
'club_group', 'admin_shell', 'admin_home', 'admin_desc',
'admin_group', 'group_desc', 'username_regex', 'groupname_regex',
'shells_file', 'server_url', 'users_base', 'groups_base',
'sasl_mech', 'sasl_realm', 'admin_bind_keytab',
'admin_bind_userid', 'realm', 'admin_principal', 'admin_keytab' ]
numeric_fields = [ 'member_min_id', 'member_max_id', 'club_min_id',
'club_max_id', 'admin_min_id', 'admin_max_id', 'group_min_id',
'group_max_id', 'min_password_length' ]
# read configuration file
cfg_tmp = conf.read(CONFIG_FILE)
# verify configuration
conf.check_string_fields(CONFIG_FILE, string_fields, cfg_tmp)
conf.check_integer_fields(CONFIG_FILE, numeric_fields, cfg_tmp)
# update the current configuration with the loaded values
cfg.update(cfg_tmp)
@ -43,6 +50,7 @@ def load_configuration():
### Exceptions ###
ConfigurationException = conf.ConfigurationException
LDAPException = ldapi.LDAPException
class MemberException(Exception):
"""Base exception class for member-related errors."""
@ -61,6 +69,14 @@ class NoSuchMember(MemberException):
def __str__(self):
return "Member not found: %d" % self.memberid
class ChildFailed(MemberException):
def __init__(self, program, status, output):
self.program, self.status, self.output = program, status, output
def __str__(self):
msg = '%s failed with status %d' % (self.program, self.status)
if self.output:
msg += ': %s' % self.output
return msg
### Connection Management ###
@ -71,7 +87,7 @@ ldap_connection = ldapi.LDAPConnection()
def connect():
"""Connect to LDAP."""
load_configuration()
configure()
ldap_connection.connect_sasl(cfg['server_url'], cfg['sasl_mech'],
cfg['sasl_realm'], cfg['admin_bind_userid'],
('keytab', cfg['admin_bind_keytab']), cfg['users_base'],
@ -92,6 +108,45 @@ def connected():
### Members ###
def create_member(username, password, name, program):
"""
Creates a UNIX user account with options tailored to CSC members.
Parameters:
username - the desired UNIX username
password - the desired UNIX password
name - the member's real name
program - the member's program of study
Exceptions:
InvalidArgument - on bad account attributes provided
Returns: the uid number of the new account
See: create()
"""
# check connection
if not connected():
raise MemberException("not connected to LDAP and Kerberos")
# check username format
if not username or not re.match(cfg['username_regex'], username):
raise InvalidArgument("username", username, "expected format %s" % repr(cfg['username_regex']))
# check password length
if not password or len(password) < cfg['min_password_length']:
raise InvalidArgument("password", "<hidden>", "too short (minimum %d characters)" % cfg['min_password_length'])
args = [ "/usr/bin/addmember", "--stdin", username, name, program ]
addmember = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = addmember.communicate(password)
status = addmember.wait()
if status:
raise ChildFailed("addmember", status, out+err)
def get(userid):
"""
Look up attributes of a member by userid.
@ -256,6 +311,39 @@ def change_group_member(action, group, userid):
ceo_ldap.modify_s(group_dn, mlist)
### Clubs ###
def create_club(username, name):
"""
Creates a UNIX user account with options tailored to CSC-hosted clubs.
Parameters:
username - the desired UNIX username
name - the club name
Exceptions:
InvalidArgument - on bad account attributes provided
Returns: the uid number of the new account
See: create()
"""
# check username format
if not username or not re.match(cfg['username_regex'], username):
raise InvalidArgument("username", username, "expected format %s" % repr(cfg['username_regex']))
args = [ "/usr/bin/addclub", username, name ]
addclub = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = addclub.communicate()
status = addclub.wait()
if status:
raise ChildFailed("addclub", status, out+err)
### Terms ###
def register(userid, term_list):

View File

@ -3,7 +3,7 @@ from csc.apps.urwid.widgets import *
from csc.apps.urwid.window import *
import csc.apps.urwid.search as search
from csc.adm import accounts, members
from csc.adm import members
from csc.common.excep import InvalidArgument
def menu_items(items):

View File

@ -3,7 +3,7 @@ import urwid
from csc.apps.urwid.widgets import *
from csc.apps.urwid.window import *
from csc.adm import accounts, members
from csc.adm import members
from csc.common.excep import InvalidArgument
class InfoPage(WizardPanel):

View File

@ -10,7 +10,7 @@ import csc.apps.urwid.search as search
import csc.apps.urwid.positions as positions
import csc.apps.urwid.groups as groups
from csc.adm import accounts, members, terms
from csc.adm import members, terms
from csc.common.excep import InvalidArgument
ui = urwid.curses_display.Screen()
@ -149,7 +149,6 @@ def manage_positions(data):
def run():
members.connect()
accounts.connect()
push_window( main_menu(), program_name() )
event_loop( ui )

View File

@ -2,7 +2,7 @@ import urwid
from csc.apps.urwid.widgets import *
from csc.apps.urwid.window import *
from csc.adm import accounts, members, terms
from csc.adm import members, terms
from csc.common.excep import InvalidArgument
class IntroPage(WizardPanel):
@ -153,23 +153,17 @@ class EndPage(WizardPanel):
problem = None
try:
if self.utype == 'member':
accounts.create_member( self.state['userid'], self.state['password'], self.state['name'], self.state['program'] )
members.create_member( self.state['userid'], self.state['password'], self.state['name'], self.state['program'] )
members.register( self.state['userid'], terms.current() )
elif self.utype == 'club':
accounts.create_club( self.state['userid'], self.state['name'] )
members.create_club( self.state['userid'], self.state['name'] )
else:
raise Exception("Internal Error")
except accounts.NameConflict, e:
except members.InvalidArgument, e:
problem = str(e)
except accounts.NoAvailableIDs, e:
except members.LDAPException, e:
problem = str(e)
except accounts.InvalidArgument, e:
problem = str(e)
except accounts.LDAPException, e:
problem = str(e)
except accounts.KrbException, e:
problem = str(e)
except accounts.ChildFailed, e:
except members.ChildFailed, e:
problem = str(e)
if problem:

View File

@ -2,7 +2,7 @@ import urwid
from csc.apps.urwid.widgets import *
from csc.apps.urwid.window import *
from csc.adm import accounts, members
from csc.adm import members
from csc.common.excep import InvalidArgument
position_data = [

View File

@ -3,7 +3,7 @@ import urwid
from csc.apps.urwid.widgets import *
from csc.apps.urwid.window import *
from csc.adm import accounts, members, terms
from csc.adm import members, terms
from csc.common.excep import InvalidArgument
class TermPage(WizardPanel):

View File

@ -3,6 +3,5 @@ Backend Modules
This module contains backend interfaces and related modules.
db - CEO database interface for member registrations
ldapi - LDAP interface for UNIX account attribute administration
"""