153 lines
4.8 KiB
Python
153 lines
4.8 KiB
Python
"""
|
|
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)
|