forked from public/pyceo
Add sasl support
This commit is contained in:
parent
a2f7888d5d
commit
7ce6543ce6
|
@ -4,7 +4,7 @@ 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
|
||||
import re, pwd, grp, os, pwd
|
||||
from csc.common import conf
|
||||
from csc.common.excep import InvalidArgument
|
||||
from csc.backends import ldapi, krb
|
||||
|
@ -18,14 +18,14 @@ 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',
|
||||
'admin_bind_dn', 'admin_bind_pw', 'realm', 'admin_principal',
|
||||
'admin_keytab' ]
|
||||
'sasl_mech', 'sasl_realm', 'admin_bind_keytab', 'admin_bind_dn',
|
||||
'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' ]
|
||||
|
@ -78,7 +78,7 @@ class NoSuchGroup(AccountException):
|
|||
self.account, self.source = account, source
|
||||
def __str__(self):
|
||||
return 'Account "%s" not found in %s' % (self.account, self.source)
|
||||
|
||||
|
||||
|
||||
|
||||
### Connection Management ###
|
||||
|
@ -86,13 +86,16 @@ class NoSuchGroup(AccountException):
|
|||
ldap_connection = ldapi.LDAPConnection()
|
||||
krb_connection = krb.KrbConnection()
|
||||
|
||||
def connect():
|
||||
def connect(auth_callback):
|
||||
"""Connect to LDAP and Kerberos and load configuration. You must call before anything else."""
|
||||
|
||||
configure()
|
||||
|
||||
# connect to the LDAP server
|
||||
ldap_connection.connect(cfg['server_url'], cfg['admin_bind_dn'], cfg['admin_bind_pw'], cfg['users_base'], cfg['groups_base'])
|
||||
ldap_connection.connect_sasl(cfg['server_url'], cfg['admin_bind_dn'],
|
||||
cfg['sasl_mech'], cfg['sasl_realm'], cfg['admin_bind_userid'],
|
||||
('keytab', cfg['admin_bind_keytab']), cfg['users_base'],
|
||||
cfg['groups_base'])
|
||||
|
||||
# connect to the Kerberos master server
|
||||
krb_connection.connect(cfg['admin_principal'], cfg['admin_keytab'])
|
||||
|
|
|
@ -9,7 +9,7 @@ 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
|
||||
import re, ldap, os, pwd
|
||||
from csc.adm import terms
|
||||
from csc.backends import ldapi
|
||||
from csc.common import conf
|
||||
|
@ -26,7 +26,8 @@ def load_configuration():
|
|||
"""Load Members Configuration"""
|
||||
|
||||
string_fields = [ 'realname_regex', 'server_url', 'users_base',
|
||||
'groups_base', 'admin_bind_dn', 'admin_bind_pw' ]
|
||||
'groups_base', 'sasl_mech', 'sasl_realm', 'admin_bind_dn',
|
||||
'admin_bind_keytab', 'admin_bind_userid' ]
|
||||
|
||||
# read configuration file
|
||||
cfg_tmp = conf.read(CONFIG_FILE)
|
||||
|
@ -74,12 +75,14 @@ class NoSuchMember(MemberException):
|
|||
# global directory connection
|
||||
ldap_connection = ldapi.LDAPConnection()
|
||||
|
||||
def connect():
|
||||
def connect(auth_callback):
|
||||
"""Connect to LDAP."""
|
||||
|
||||
load_configuration()
|
||||
ldap_connection.connect(cfg['server_url'], cfg['admin_bind_dn'], cfg['admin_bind_pw'], cfg['users_base'], cfg['groups_base'])
|
||||
|
||||
ldap_connection.connect_sasl(cfg['server_url'], cfg['admin_bind_dn'],
|
||||
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."""
|
||||
|
|
|
@ -13,7 +13,7 @@ have an LDAP entry, even if the account does not log in directly.
|
|||
This module makes use of python-ldap, a Python module with bindings
|
||||
to libldap, OpenLDAP's native C client library.
|
||||
"""
|
||||
import ldap.modlist
|
||||
import ldap.modlist, ipc, os
|
||||
|
||||
|
||||
class LDAPException(Exception):
|
||||
|
@ -41,36 +41,39 @@ class LDAPConnection(object):
|
|||
def __init__(self):
|
||||
self.ldap = None
|
||||
|
||||
|
||||
def connect(self, server, bind_dn, bind_pw, user_base, group_base):
|
||||
|
||||
def connect_anon(self, uri, user_base, group_base):
|
||||
"""
|
||||
Establish a connection to the LDAP Server.
|
||||
|
||||
Parameters:
|
||||
server - connection string (e.g. ldap://foo.com, ldaps://bar.com)
|
||||
bind_dn - distinguished name to bind to
|
||||
bind_pw - password of bind_dn
|
||||
uri - connection string (e.g. ldap://foo.com, ldaps://bar.com)
|
||||
user_base - base of the users subtree
|
||||
group_base - baes of the group subtree
|
||||
|
||||
Example: connect('ldaps:///', 'cn=ceo,dc=csclub,dc=uwaterloo,dc=ca',
|
||||
'secret', 'ou=People,dc=csclub,dc=uwaterloo,dc=ca',
|
||||
'ou=Group,dc=csclub,dc=uwaterloo,dc=ca')
|
||||
|
||||
|
||||
"""
|
||||
|
||||
if bind_pw is None: bind_pw = ''
|
||||
# open the connection
|
||||
self.ldap = ldap.initialize(uri)
|
||||
|
||||
try:
|
||||
# authenticate
|
||||
self.ldap.simple_bind_s('', '')
|
||||
|
||||
# open the connection
|
||||
self.ldap = ldap.initialize(server)
|
||||
self.user_base = user_base
|
||||
self.group_base = group_base
|
||||
|
||||
# authenticate
|
||||
self.ldap.simple_bind_s(bind_dn, bind_pw)
|
||||
def connect_sasl(self, uri, bind_dn, mech, realm, userid, password, user_base, group_base):
|
||||
|
||||
except ldap.LDAPError, e:
|
||||
raise LDAPException("unable to connect: %s" % e)
|
||||
# open the connection
|
||||
self.ldap = ldap.initialize(uri)
|
||||
|
||||
# authenticate
|
||||
sasl = Sasl(mech, realm, userid, password)
|
||||
self.ldap.sasl_interactive_bind_s(bind_dn, sasl)
|
||||
|
||||
self.user_base = user_base
|
||||
self.group_base = group_base
|
||||
|
@ -659,6 +662,31 @@ class LDAPConnection(object):
|
|||
return mlist
|
||||
|
||||
|
||||
class Sasl:
|
||||
|
||||
CB_USER = 0x4001
|
||||
bind_dn = 'dn:uid=%s,cn=%s,cn=%s,cn=auth'
|
||||
|
||||
def __init__(self, mech, realm, userid, password):
|
||||
self.mech = mech
|
||||
self.bind_dn = self.bind_dn % (userid, realm, mech)
|
||||
|
||||
if mech == 'GSSAPI':
|
||||
type, arg = password
|
||||
kinit = '/usr/bin/kinit'
|
||||
kinit_args = [ 'kinit', '%s@%s' % (userid, realm) ]
|
||||
if type == 'keytab':
|
||||
kinit_args += [ '-k', '-t', arg ]
|
||||
pid, kinit_out, kinit_in = ipc.popeni(kinit, kinit_args)
|
||||
os.waitpid(pid, 0)
|
||||
|
||||
def callback(self, id, challenge, prompt, defresult):
|
||||
if id == self.CB_USER:
|
||||
return self.bind_dn
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
### Tests ###
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Reference in New Issue