pyceo/pylib/csc/adm/accounts.py

233 lines
6.7 KiB
Python

# $Id: accounts.py 44 2006-12-31 07:09:27Z mspang $
# UNIX Accounts Module
import re
from csc.backends import ldapi, krb
from csc.common.conf import read_config
CONFIG_FILE = '/etc/csc/accounts.cf'
cfg = {}
# error constants
SUCCESS = 0
LDAP_EXISTS = 1
LDAP_NO_IDS = 2
LDAP_NO_USER = 3
KRB_EXISTS = 5
KRB_NO_USER = 6
BAD_USERNAME = 8
BAD_REALNAME = 9
# error messages
errors = [ "Success", "LDAP: entry exists",
"LDAP: no user ids available", "LDAP: no such entry",
"KRB: principal exists", "KRB: no such principal",
"Invalid username", "Invalid real name"]
class AccountException(Exception):
"""Exception class for account-related errors."""
def load_configuration():
"""Load Accounts Configuration."""
# configuration already loaded?
if len(cfg) > 0:
return
# read in the file
cfg_tmp = read_config(CONFIG_FILE)
if not cfg_tmp:
raise AccountException("unable to read configuration file: %s" % CONFIG_FILE)
# check that essential fields are completed
mandatory_fields = [ 'minimum_id', 'maximum_id', 'shell', 'home',
'gid', 'server_url', 'users_base', 'groups_base', 'bind_dn',
'bind_password', 'realm', 'principal', 'keytab', 'username_regex',
'realname_regex'
]
for field in mandatory_fields:
if not field in cfg_tmp:
raise AccountException("missing configuration option: %s" % field)
if not cfg_tmp[field]:
raise AccountException("null configuration option: %s" % field)
# check that numeric fields are ints
numeric_fields = [ 'minimum_id', 'maximum_id', 'gid' ]
for field in numeric_fields:
if not type(cfg_tmp[field]) in (int, long):
raise AccountException("non-numeric value for configuration option: %s" % field)
# update the current configuration with the loaded values
cfg.update(cfg_tmp)
def create_account(username, password, realname='', gecos_other=''):
"""
Creates a UNIX account for a member. This involves
first creating a directory entry, then creating
a Kerberos principal.
Parameters:
username - UNIX username for the member
realname - real name of the member
password - password for the account
Exceptions:
LDAPException - on LDAP failure
KrbException - on Kerberos failure
Returns:
SUCCESS - on success
BAD_REALNAME - on badly formed real name
BAD_USERNAME - on badly formed user name
LDAP_EXISTS - when the user exists in LDAP
LDAP_NO_IDS - when no user ids are free
KRB_EXISTS - when the user exists in Kerberos
"""
# Load Configuration
load_configuration()
### Connect to the Backends ###
ldap_connection = ldapi.LDAPConnection()
krb_connection = krb.KrbConnection()
try:
# connect to the LDAP server
ldap_connection.connect(cfg['server_url'], cfg['bind_dn'], cfg['bind_password'], cfg['users_base'], cfg['groups_base'])
# connect to the Kerberos master server
krb_connection.connect(cfg['principal'], cfg['keytab'])
### Sanity-checks ###
# check the username and realame for validity
if not re.match(cfg['username_regex'], username):
return BAD_USERNAME
if not re.match(cfg['realname_regex'], realname):
return BAD_REALNAME
# see if user exists in LDAP
if ldap_connection.user_lookup(username):
return LDAP_EXISTS
# determine the first available userid
userid = ldap_connection.first_id(cfg['minimum_id'], cfg['maximum_id'])
if not userid: return LDAP_NO_IDS
# build principal name from username
principal = username + '@' + cfg['realm']
# see if user exists in Kerberos
if krb_connection.get_principal(principal):
return KRB_EXISTS
### User creation ###
# process gecos_other (used to store memberid)
if gecos_other:
gecos_other = ',' + str(gecos_other)
# account information defaults
shell = cfg['shell']
home = cfg['home'] + '/' + username
gecos = realname + ',,,' + gecos_other
gid = cfg['gid']
# create the LDAP entry
ldap_connection.user_add(username, realname, shell, userid, gid, home, gecos)
# create the Kerberos principal
krb_connection.add_principal(principal, password)
finally:
ldap_connection.disconnect()
krb_connection.disconnect()
return SUCCESS
def delete_account(username):
"""
Deletes the UNIX account of a member.
Parameters:
username - UNIX username for the member
Exceptions:
LDAPException - on LDAP failure
KrbException - on Kerberos failure
Returns:
SUCCESS - on success
LDAP_NO_USER - when the user does not exist in LDAP
KRB_NO_USER - when the user does not exist in Kerberos
"""
# Load Configuration
load_configuration()
### Connect to the Backends ###
ldap_connection = ldapi.LDAPConnection()
krb_connection = krb.KrbConnection()
try:
# connect to the LDAP server
ldap_connection.connect(cfg['server_url'], cfg['bind_dn'], cfg['bind_password'], cfg['users_base'], cfg['groups_base'])
# connect to the Kerberos master server
krb_connection.connect(cfg['principal'], cfg['keytab'])
### Sanity-checks ###
# ensure user exists in LDAP
if not ldap_connection.user_lookup(username):
return LDAP_NO_USER
# build principal name from username
principal = username + '@' + cfg['realm']
# see if user exists in Kerberos
if not krb_connection.get_principal(principal):
return KRB_NO_USER
### User deletion ###
# delete the LDAP entry
ldap_connection.user_delete(username)
# delete the Kerberos principal
krb_connection.delete_principal(principal)
finally:
ldap_connection.disconnect()
krb_connection.disconnect()
return SUCCESS
### Tests ###
if __name__ == '__main__':
# A word of notice: this test creates a _working_ account (and then deletes it).
# If deletion fails it must be cleaned up manually.
# a bit of salt so the test account is reasonably tough to crack
import random
pw = str(random.randint(100000000000000000, 999999999999999999))
print "running create_account('testuser', ..., 'Test User', ...)", "->", errors[create_account('testuser', pw, 'Test User')]
print "running delete_account('testuser')", "->", errors[delete_account('testuser')]