|
|
|
|
# Authors: Rob Crittenden <rcritten@redhat.com>
|
|
|
|
|
#
|
|
|
|
|
# Copyright (C) 2007 Red Hat
|
|
|
|
|
# see file 'COPYING' for use and warranty information
|
|
|
|
|
#
|
|
|
|
|
# This program is free software; you can redistribute it and/or
|
|
|
|
|
# modify it under the terms of the GNU General Public License as
|
|
|
|
|
# published by the Free Software Foundation; version 2 only
|
|
|
|
|
#
|
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
|
#
|
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
import sys
|
|
|
|
|
sys.path.append("/usr/share/ipa")
|
|
|
|
|
|
2007-10-01 17:33:16 -04:00
|
|
|
import krbV
|
|
|
|
|
import ldap
|
2007-10-05 15:25:58 -07:00
|
|
|
import ldap.dn
|
|
|
|
|
import ipaserver.dsinstance
|
|
|
|
|
import ipaserver.ipaldap
|
2007-09-04 16:13:15 -04:00
|
|
|
import ipa.ipautil
|
|
|
|
|
import xmlrpclib
|
2007-08-24 15:42:56 -04:00
|
|
|
import copy
|
2007-10-22 17:06:52 -04:00
|
|
|
import attrs
|
2007-08-22 10:30:51 -07:00
|
|
|
from ipa import ipaerror
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
from ipa import radius_util
|
2007-08-22 10:30:51 -07:00
|
|
|
|
|
|
|
|
import string
|
|
|
|
|
from types import *
|
2007-08-13 16:41:38 -04:00
|
|
|
import os
|
2007-08-21 14:26:36 -07:00
|
|
|
import re
|
2007-08-13 16:41:38 -04:00
|
|
|
|
2007-10-08 16:18:38 -04:00
|
|
|
try:
|
|
|
|
|
from threading import Lock
|
|
|
|
|
except ImportError:
|
|
|
|
|
from dummy_threading import Lock
|
|
|
|
|
|
2007-08-13 16:41:38 -04:00
|
|
|
# Need a global to store this between requests
|
|
|
|
|
_LDAPPool = None
|
|
|
|
|
|
2007-10-12 15:11:55 -07:00
|
|
|
ACIContainer = "cn=accounts"
|
2007-08-29 18:07:05 -04:00
|
|
|
DefaultUserContainer = "cn=users,cn=accounts"
|
|
|
|
|
DefaultGroupContainer = "cn=groups,cn=accounts"
|
2007-08-23 09:44:00 -04:00
|
|
|
|
2007-08-13 16:41:38 -04:00
|
|
|
#
|
|
|
|
|
# Apache runs in multi-process mode so each process will have its own
|
|
|
|
|
# connection. This could theoretically drive the total number of connections
|
|
|
|
|
# very high but since this represents just the administrative interface
|
|
|
|
|
# this is not anticipated.
|
2007-10-08 16:18:38 -04:00
|
|
|
#
|
|
|
|
|
# The pool consists of two things, a dictionary keyed on the principal name
|
|
|
|
|
# that contains the connection and a list that is used to keep track of the
|
|
|
|
|
# order. If the list fills up just pop the top entry off and you've got
|
|
|
|
|
# the least recently used.
|
2007-08-13 16:41:38 -04:00
|
|
|
|
2007-10-08 16:18:38 -04:00
|
|
|
# maxsize = 0 means no limit
|
|
|
|
|
class IPAConnPool:
|
|
|
|
|
def __init__(self, maxsize = 0):
|
|
|
|
|
self._dict = {}
|
|
|
|
|
self._lru = []
|
|
|
|
|
self._lock = Lock()
|
|
|
|
|
self._maxsize = maxsize
|
|
|
|
|
self._ctx = krbV.default_context()
|
|
|
|
|
|
|
|
|
|
def getConn(self, host, port, krbccache=None, debug=None):
|
2007-09-05 13:14:23 -04:00
|
|
|
conn = None
|
2007-10-12 10:37:36 -04:00
|
|
|
|
2007-10-08 16:18:38 -04:00
|
|
|
ccache = krbV.CCache(name=krbccache, context=self._ctx)
|
|
|
|
|
cprinc = ccache.principal()
|
2007-10-12 10:37:36 -04:00
|
|
|
|
|
|
|
|
conn = ipaserver.ipaldap.IPAdmin(host,port,None,None,None,debug)
|
|
|
|
|
|
|
|
|
|
# This will bind the connection
|
|
|
|
|
conn.set_krbccache(krbccache, cprinc.name)
|
2007-10-08 16:18:38 -04:00
|
|
|
|
2007-08-13 16:41:38 -04:00
|
|
|
return conn
|
|
|
|
|
|
|
|
|
|
def releaseConn(self, conn):
|
2007-09-26 15:47:34 -07:00
|
|
|
if conn is None:
|
|
|
|
|
return
|
2007-10-08 16:18:38 -04:00
|
|
|
|
2007-10-12 10:37:36 -04:00
|
|
|
conn.unbind_s()
|
|
|
|
|
|
2007-08-06 10:05:53 -04:00
|
|
|
class IPAServer:
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
2007-08-13 16:41:38 -04:00
|
|
|
global _LDAPPool
|
2007-08-06 10:05:53 -04:00
|
|
|
# FIXME, this needs to be auto-discovered
|
|
|
|
|
self.host = 'localhost'
|
2007-09-05 13:14:23 -04:00
|
|
|
self.port = 389
|
|
|
|
|
self.sslport = 636
|
2007-08-06 10:05:53 -04:00
|
|
|
self.bindcert = "/usr/share/ipa/cert.pem"
|
|
|
|
|
self.bindkey = "/usr/share/ipa/key.pem"
|
|
|
|
|
self.bindca = "/usr/share/ipa/cacert.asc"
|
2007-10-01 17:33:16 -04:00
|
|
|
self.krbctx = krbV.default_context()
|
|
|
|
|
self.realm = self.krbctx.default_realm
|
|
|
|
|
|
2007-08-13 16:41:38 -04:00
|
|
|
if _LDAPPool is None:
|
2007-10-08 16:18:38 -04:00
|
|
|
_LDAPPool = IPAConnPool(128)
|
2007-10-01 17:33:16 -04:00
|
|
|
self.basedn = ipa.ipautil.realm_to_suffix(self.realm)
|
2007-08-06 10:05:53 -04:00
|
|
|
self.scope = ldap.SCOPE_SUBTREE
|
|
|
|
|
self.princ = None
|
2007-09-14 17:19:02 -04:00
|
|
|
self.krbccache = None
|
2007-08-06 10:05:53 -04:00
|
|
|
|
|
|
|
|
def set_principal(self, princ):
|
|
|
|
|
self.princ = princ
|
2007-09-05 13:14:23 -04:00
|
|
|
|
2007-09-14 17:19:02 -04:00
|
|
|
def set_krbccache(self, krbccache):
|
|
|
|
|
self.krbccache = krbccache
|
2007-08-06 10:05:53 -04:00
|
|
|
|
2007-09-21 14:39:52 -04:00
|
|
|
def get_dn_from_principal(self, princ, debug):
|
2007-09-05 13:14:23 -04:00
|
|
|
"""Given a kerberos principal get the LDAP uid"""
|
2007-08-13 16:41:38 -04:00
|
|
|
global _LDAPPool
|
|
|
|
|
|
2007-09-27 16:07:05 -07:00
|
|
|
princ = self.__safe_filter(princ)
|
2007-08-06 10:05:53 -04:00
|
|
|
filter = "(krbPrincipalName=" + princ + ")"
|
2007-08-22 10:30:51 -07:00
|
|
|
# The only anonymous search we should have
|
2007-09-21 14:39:52 -04:00
|
|
|
conn = _LDAPPool.getConn(self.host,self.sslport,self.bindca,self.bindcert,self.bindkey,None,None,debug)
|
2007-08-27 11:30:26 -07:00
|
|
|
try:
|
2007-09-05 13:14:23 -04:00
|
|
|
ent = conn.getEntry(self.basedn, self.scope, filter, ['dn'])
|
2007-08-27 11:30:26 -07:00
|
|
|
finally:
|
2007-09-05 13:14:23 -04:00
|
|
|
_LDAPPool.releaseConn(conn)
|
2007-08-06 10:05:53 -04:00
|
|
|
|
|
|
|
|
return "dn:" + ent.dn
|
2007-08-24 15:42:56 -04:00
|
|
|
|
2007-09-05 13:14:23 -04:00
|
|
|
def __setup_connection(self, opts):
|
|
|
|
|
"""Set up common things done in the connection.
|
2007-09-14 17:19:02 -04:00
|
|
|
If there is a Kerberos credentials cache then return None as the
|
|
|
|
|
proxy dn and the ccache otherwise return the proxy dn and None as
|
|
|
|
|
the ccache.
|
2007-09-05 13:14:23 -04:00
|
|
|
|
|
|
|
|
We only want one or the other used at one time and we prefer
|
2007-09-14 17:19:02 -04:00
|
|
|
the Kerberos credentials cache. So if there is a ccache, return
|
|
|
|
|
that and None for proxy dn to make calling getConn() easier.
|
2007-09-05 13:14:23 -04:00
|
|
|
"""
|
|
|
|
|
|
2007-09-25 08:36:23 -04:00
|
|
|
debug = "Off"
|
2007-09-21 14:39:52 -04:00
|
|
|
|
2007-09-25 08:36:23 -04:00
|
|
|
if opts is not None:
|
|
|
|
|
debug = opts.get('ipadebug')
|
2007-09-14 17:19:02 -04:00
|
|
|
if opts.get('krbccache'):
|
|
|
|
|
self.set_krbccache(opts['krbccache'])
|
2007-09-05 13:14:23 -04:00
|
|
|
self.set_principal(None)
|
|
|
|
|
else:
|
2007-09-14 17:19:02 -04:00
|
|
|
self.set_krbccache(None)
|
2007-09-05 13:14:23 -04:00
|
|
|
self.set_principal(opts['remoteuser'])
|
|
|
|
|
else:
|
2007-09-14 17:19:02 -04:00
|
|
|
# The caller should have already set the principal or the
|
|
|
|
|
# krbccache. If not they'll get an authentication error later.
|
|
|
|
|
pass
|
2007-09-05 13:14:23 -04:00
|
|
|
|
|
|
|
|
if self.princ is not None:
|
2007-09-21 14:39:52 -04:00
|
|
|
return self.get_dn_from_principal(self.princ, debug), None, debug
|
2007-09-05 13:14:23 -04:00
|
|
|
else:
|
2007-09-21 14:39:52 -04:00
|
|
|
return None, self.krbccache, debug
|
2007-09-05 13:14:23 -04:00
|
|
|
|
|
|
|
|
def getConnection(self, opts):
|
|
|
|
|
"""Wrapper around IPAConnPool.getConn() so we don't have to pass
|
|
|
|
|
around self.* every time a connection is needed.
|
|
|
|
|
|
2007-09-14 17:19:02 -04:00
|
|
|
For SASL connections (where we have a krbccache) we can't set
|
2007-09-05 13:14:23 -04:00
|
|
|
the SSL variables for certificates. It confuses the ldap
|
|
|
|
|
module.
|
|
|
|
|
"""
|
|
|
|
|
global _LDAPPool
|
|
|
|
|
|
2007-09-21 14:39:52 -04:00
|
|
|
(proxy_dn, krbccache, debug) = self.__setup_connection(opts)
|
2007-09-05 13:14:23 -04:00
|
|
|
|
2007-09-14 17:19:02 -04:00
|
|
|
if krbccache is not None:
|
2007-09-05 13:14:23 -04:00
|
|
|
bindca = None
|
|
|
|
|
bindcert = None
|
|
|
|
|
bindkey = None
|
|
|
|
|
port = self.port
|
|
|
|
|
else:
|
2007-10-02 16:56:51 -04:00
|
|
|
raise ipaerror.gen_exception(ipaerror.CONNECTION_NO_CCACHE)
|
2007-09-05 13:14:23 -04:00
|
|
|
|
2007-10-03 17:37:13 -04:00
|
|
|
try:
|
2007-10-08 16:18:38 -04:00
|
|
|
conn = _LDAPPool.getConn(self.host,port,krbccache,debug)
|
2007-10-03 17:37:13 -04:00
|
|
|
except ldap.INVALID_CREDENTIALS, e:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.CONNECTION_GSSAPI_CREDENTIALS, nested_exception=e)
|
2007-10-02 16:56:51 -04:00
|
|
|
|
|
|
|
|
if conn is None:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.CONNECTION_NO_CONN)
|
|
|
|
|
|
|
|
|
|
return conn
|
2007-09-05 13:14:23 -04:00
|
|
|
|
|
|
|
|
def releaseConnection(self, conn):
|
|
|
|
|
global _LDAPPool
|
|
|
|
|
|
|
|
|
|
_LDAPPool.releaseConn(conn)
|
|
|
|
|
|
2007-08-06 10:05:53 -04:00
|
|
|
def convert_entry(self, ent):
|
|
|
|
|
entry = dict(ent.data)
|
|
|
|
|
entry['dn'] = ent.dn
|
|
|
|
|
# For now convert single entry lists to a string for the ui.
|
|
|
|
|
# TODO: we need to deal with multi-values better
|
|
|
|
|
for key,value in entry.iteritems():
|
|
|
|
|
if isinstance(value,list) or isinstance(value,tuple):
|
|
|
|
|
if len(value) == 0:
|
|
|
|
|
entry[key] = ''
|
|
|
|
|
elif len(value) == 1:
|
|
|
|
|
entry[key] = value[0]
|
|
|
|
|
return entry
|
2007-08-23 09:44:00 -04:00
|
|
|
|
2007-10-11 10:10:03 -07:00
|
|
|
# TODO: rethink the get_entry vs get_list API calls.
|
|
|
|
|
# they currently restrict the data coming back without
|
|
|
|
|
# restricting scope. For now adding a __get_base/sub_entry()
|
|
|
|
|
# calls, but the API isn't great.
|
|
|
|
|
def __get_entry (self, base, scope, filter, sattrs=None, opts=None):
|
|
|
|
|
"""Get a specific entry (with a parametized scope).
|
|
|
|
|
Return as a dict of values.
|
2007-08-06 10:05:53 -04:00
|
|
|
Multi-valued fields are represented as lists.
|
|
|
|
|
"""
|
|
|
|
|
ent=""
|
2007-08-17 10:03:33 -04:00
|
|
|
|
2007-09-05 13:14:23 -04:00
|
|
|
conn = self.getConnection(opts)
|
2007-08-27 11:30:26 -07:00
|
|
|
try:
|
2007-10-11 10:10:03 -07:00
|
|
|
ent = conn.getEntry(base, scope, filter, sattrs)
|
|
|
|
|
|
2007-08-27 11:30:26 -07:00
|
|
|
finally:
|
2007-09-05 13:14:23 -04:00
|
|
|
self.releaseConnection(conn)
|
2007-10-11 10:10:03 -07:00
|
|
|
|
2007-08-06 10:05:53 -04:00
|
|
|
return self.convert_entry(ent)
|
2007-08-24 15:42:56 -04:00
|
|
|
|
2007-10-11 10:10:03 -07:00
|
|
|
def __get_base_entry (self, base, filter, sattrs=None, opts=None):
|
|
|
|
|
"""Get a specific entry (with a scope of BASE).
|
|
|
|
|
Return as a dict of values.
|
|
|
|
|
Multi-valued fields are represented as lists.
|
|
|
|
|
"""
|
|
|
|
|
return self.__get_entry(base, ldap.SCOPE_BASE, filter, sattrs, opts)
|
|
|
|
|
|
|
|
|
|
def __get_sub_entry (self, base, filter, sattrs=None, opts=None):
|
|
|
|
|
"""Get a specific entry (with a scope of SUB).
|
|
|
|
|
Return as a dict of values.
|
|
|
|
|
Multi-valued fields are represented as lists.
|
|
|
|
|
"""
|
|
|
|
|
return self.__get_entry(base, ldap.SCOPE_SUBTREE, filter, sattrs, opts)
|
|
|
|
|
|
2007-09-25 13:35:43 -07:00
|
|
|
def __get_list (self, base, filter, sattrs=None, opts=None):
|
|
|
|
|
"""Gets a list of entries. Each is converted to a dict of values.
|
|
|
|
|
Multi-valued fields are represented as lists.
|
|
|
|
|
"""
|
|
|
|
|
entries = []
|
|
|
|
|
|
|
|
|
|
conn = self.getConnection(opts)
|
|
|
|
|
try:
|
|
|
|
|
entries = conn.getList(base, self.scope, filter, sattrs)
|
|
|
|
|
finally:
|
|
|
|
|
self.releaseConnection(conn)
|
|
|
|
|
|
|
|
|
|
return map(self.convert_entry, entries)
|
|
|
|
|
|
2007-08-24 15:42:56 -04:00
|
|
|
def __update_entry (self, oldentry, newentry, opts=None):
|
|
|
|
|
"""Update an LDAP entry
|
|
|
|
|
|
|
|
|
|
oldentry is a dict
|
|
|
|
|
newentry is a dict
|
|
|
|
|
"""
|
|
|
|
|
oldentry = self.convert_scalar_values(oldentry)
|
|
|
|
|
newentry = self.convert_scalar_values(newentry)
|
|
|
|
|
|
|
|
|
|
# Should be able to get this from either the old or new entry
|
|
|
|
|
# but just in case someone has decided to try changing it, use the
|
|
|
|
|
# original
|
|
|
|
|
try:
|
|
|
|
|
moddn = oldentry['dn']
|
|
|
|
|
except KeyError, e:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_MISSING_DN)
|
|
|
|
|
|
2007-09-05 13:14:23 -04:00
|
|
|
conn = self.getConnection(opts)
|
2007-08-27 11:30:26 -07:00
|
|
|
try:
|
2007-09-05 13:14:23 -04:00
|
|
|
res = conn.updateEntry(moddn, oldentry, newentry)
|
2007-08-27 11:30:26 -07:00
|
|
|
finally:
|
2007-09-05 13:14:23 -04:00
|
|
|
self.releaseConnection(conn)
|
2007-08-24 15:42:56 -04:00
|
|
|
return res
|
|
|
|
|
|
|
|
|
|
def __safe_filter(self, criteria):
|
|
|
|
|
"""Make sure any arguments used when creating a filter are safe."""
|
|
|
|
|
|
|
|
|
|
# TODO: this escaper assumes the python-ldap library will error out
|
|
|
|
|
# on invalid codepoints. we need to check malformed utf-8 input
|
|
|
|
|
# where the second byte in a multi-byte character
|
|
|
|
|
# is (illegally) ')' and make sure python-ldap
|
|
|
|
|
# bombs out.
|
2007-08-27 11:30:26 -07:00
|
|
|
criteria = re.sub(r'[\(\)\\\*]', ldap_search_escape, criteria)
|
2007-08-24 15:42:56 -04:00
|
|
|
|
|
|
|
|
return criteria
|
2007-08-27 11:30:26 -07:00
|
|
|
|
|
|
|
|
def __generate_match_filters(self, search_fields, criteria_words):
|
|
|
|
|
"""Generates a search filter based on a list of words and a list
|
|
|
|
|
of fields to search against.
|
|
|
|
|
|
|
|
|
|
Returns a tuple of two filters: (exact_match, partial_match)"""
|
|
|
|
|
|
|
|
|
|
# construct search pattern for a single word
|
|
|
|
|
# (|(f1=word)(f2=word)...)
|
|
|
|
|
search_pattern = "(|"
|
|
|
|
|
for field in search_fields:
|
|
|
|
|
search_pattern += "(" + field + "=%(match)s)"
|
|
|
|
|
search_pattern += ")"
|
|
|
|
|
gen_search_pattern = lambda word: search_pattern % {'match':word}
|
|
|
|
|
|
|
|
|
|
# construct the giant match for all words
|
|
|
|
|
exact_match_filter = "(&"
|
|
|
|
|
partial_match_filter = "(&"
|
|
|
|
|
for word in criteria_words:
|
|
|
|
|
exact_match_filter += gen_search_pattern(word)
|
|
|
|
|
partial_match_filter += gen_search_pattern("*%s*" % word)
|
|
|
|
|
exact_match_filter += ")"
|
|
|
|
|
partial_match_filter += ")"
|
|
|
|
|
|
|
|
|
|
return (exact_match_filter, partial_match_filter)
|
2007-10-09 09:26:16 -07:00
|
|
|
|
2007-10-12 15:11:55 -07:00
|
|
|
# Higher-level API
|
|
|
|
|
|
|
|
|
|
def get_aci_entry(self, sattrs=None, opts=None):
|
|
|
|
|
"""Returns the entry containing access control ACIs."""
|
|
|
|
|
|
|
|
|
|
dn="%s,%s" % (ACIContainer, self.basedn)
|
|
|
|
|
return self.get_entry_by_dn(dn, sattrs, opts)
|
|
|
|
|
|
2007-10-09 09:26:16 -07:00
|
|
|
# General searches
|
|
|
|
|
|
|
|
|
|
def get_entry_by_dn (self, dn, sattrs=None, opts=None):
|
|
|
|
|
"""Get a specific entry. Return as a dict of values.
|
|
|
|
|
Multi-valued fields are represented as lists.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
filter = "(objectClass=*)"
|
2007-10-11 10:10:03 -07:00
|
|
|
return self.__get_base_entry(dn, filter, sattrs, opts)
|
2007-10-09 09:26:16 -07:00
|
|
|
|
|
|
|
|
def get_entry_by_cn (self, cn, sattrs=None, opts=None):
|
|
|
|
|
"""Get a specific entry by cn. Return as a dict of values.
|
|
|
|
|
Multi-valued fields are represented as lists.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
cn = self.__safe_filter(cn)
|
|
|
|
|
filter = "(cn=" + cn + ")"
|
2007-10-11 10:10:03 -07:00
|
|
|
return self.__get_sub_entry(self.basedn, filter, sattrs, opts)
|
2007-10-09 09:26:16 -07:00
|
|
|
|
2007-10-15 09:04:13 -07:00
|
|
|
def update_entry (self, oldentry, newentry, opts=None):
|
|
|
|
|
"""Update an entry in LDAP"""
|
|
|
|
|
return self.__update_entry(oldentry, newentry, opts)
|
|
|
|
|
|
2007-08-24 15:42:56 -04:00
|
|
|
# User support
|
|
|
|
|
|
|
|
|
|
def __is_user_unique(self, uid, opts):
|
|
|
|
|
"""Return 1 if the uid is unique in the tree, 0 otherwise."""
|
|
|
|
|
uid = self.__safe_filter(uid)
|
|
|
|
|
filter = "(&(uid=%s)(objectclass=posixAccount))" % uid
|
|
|
|
|
|
2007-08-27 13:45:28 -04:00
|
|
|
try:
|
2007-10-11 10:10:03 -07:00
|
|
|
entry = self.__get_sub_entry(self.basedn, filter, ['dn','uid'], opts)
|
2007-08-24 15:42:56 -04:00
|
|
|
return 0
|
2007-08-27 13:45:28 -04:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
2007-08-24 15:42:56 -04:00
|
|
|
return 1
|
|
|
|
|
|
2007-08-23 09:44:00 -04:00
|
|
|
def get_user_by_uid (self, uid, sattrs=None, opts=None):
|
|
|
|
|
"""Get a specific user's entry. Return as a dict of values.
|
|
|
|
|
Multi-valued fields are represented as lists.
|
|
|
|
|
"""
|
|
|
|
|
|
2007-08-24 15:42:56 -04:00
|
|
|
uid = self.__safe_filter(uid)
|
2007-08-23 09:44:00 -04:00
|
|
|
filter = "(uid=" + uid + ")"
|
2007-10-11 10:10:03 -07:00
|
|
|
return self.__get_sub_entry(self.basedn, filter, sattrs, opts)
|
2007-09-25 15:44:49 -07:00
|
|
|
|
2007-10-01 17:33:16 -04:00
|
|
|
def get_user_by_principal(self, principal, sattrs=None, opts=None):
|
|
|
|
|
"""Get a user entry searching by Kerberos Principal Name.
|
|
|
|
|
Return as a dict of values. Multi-valued fields are
|
|
|
|
|
represented as lists.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
filter = "(krbPrincipalName="+self.__safe_filter(principal)+")"
|
2007-10-11 10:10:03 -07:00
|
|
|
return self.__get_sub_entry(self.basedn, filter, sattrs, opts)
|
2007-10-18 14:33:55 -07:00
|
|
|
|
|
|
|
|
def get_user_by_email (self, email, sattrs=None, opts=None):
|
|
|
|
|
"""Get a specific user's entry. Return as a dict of values.
|
|
|
|
|
Multi-valued fields are represented as lists.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
email = self.__safe_filter(email)
|
|
|
|
|
filter = "(mail=" + email + ")"
|
|
|
|
|
return self.__get_sub_entry(self.basedn, filter, sattrs, opts)
|
|
|
|
|
|
2007-09-25 15:44:49 -07:00
|
|
|
def get_users_by_manager (self, manager_dn, sattrs=None, opts=None):
|
|
|
|
|
"""Gets the users that report to a particular manager.
|
|
|
|
|
"""
|
|
|
|
|
|
2007-09-27 16:07:05 -07:00
|
|
|
manager_dn = self.__safe_filter(manager_dn)
|
2007-09-25 15:44:49 -07:00
|
|
|
filter = "(&(objectClass=person)(manager=%s))" % manager_dn
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
return self.__get_list(self.basedn, filter, sattrs, opts)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
return []
|
|
|
|
|
|
2007-08-23 09:44:00 -04:00
|
|
|
def add_user (self, user, user_container=None, opts=None):
|
2007-08-17 10:03:33 -04:00
|
|
|
"""Add a user in LDAP. Takes as input a dict where the key is the
|
|
|
|
|
attribute name and the value is either a string or in the case
|
|
|
|
|
of a multi-valued field a list of values. user_container sets
|
|
|
|
|
where in the tree the user is placed."""
|
2007-08-23 09:44:00 -04:00
|
|
|
if user_container is None:
|
2007-08-24 15:42:56 -04:00
|
|
|
user_container = DefaultUserContainer
|
|
|
|
|
|
|
|
|
|
if self.__is_user_unique(user['uid'], opts) == 0:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
|
2007-08-17 14:27:54 -07:00
|
|
|
|
2007-10-05 15:25:58 -07:00
|
|
|
dn="uid=%s,%s,%s" % (ldap.dn.escape_dn_chars(user['uid']),
|
|
|
|
|
user_container,self.basedn)
|
2007-08-17 15:32:05 -07:00
|
|
|
entry = ipaserver.ipaldap.Entry(dn)
|
2007-08-06 10:05:53 -04:00
|
|
|
|
2007-08-23 11:57:25 -04:00
|
|
|
# FIXME: This should be dynamic and can include just about anything
|
|
|
|
|
|
|
|
|
|
# Let us add in some missing attributes
|
|
|
|
|
if user.get('homedirectory') is None:
|
2007-10-01 17:33:16 -04:00
|
|
|
user['homedirectory'] = '/home/%s' % user.get('uid')
|
2007-10-03 13:53:14 -07:00
|
|
|
if user.get('gecos') is None:
|
2007-10-01 17:33:16 -04:00
|
|
|
user['gecos'] = user['uid']
|
2007-08-23 11:57:25 -04:00
|
|
|
|
|
|
|
|
# FIXME: This can be removed once the DS plugin is installed
|
|
|
|
|
user['uidnumber'] = '501'
|
|
|
|
|
|
|
|
|
|
# FIXME: What is the default group for users?
|
|
|
|
|
user['gidnumber'] = '501'
|
|
|
|
|
|
2007-10-01 17:33:16 -04:00
|
|
|
if user.get('krbprincipalname') is None:
|
|
|
|
|
user['krbprincipalname'] = "%s@%s" % (user.get('uid'), self.realm)
|
2007-08-23 11:57:25 -04:00
|
|
|
|
|
|
|
|
# FIXME. This is a hack so we can request separate First and Last
|
|
|
|
|
# name in the GUI.
|
|
|
|
|
if user.get('cn') is None:
|
|
|
|
|
user['cn'] = "%s %s" % (user.get('givenname'),
|
|
|
|
|
user.get('sn'))
|
|
|
|
|
|
|
|
|
|
if user.get('gn'):
|
|
|
|
|
del user['gn']
|
|
|
|
|
|
2007-08-06 10:05:53 -04:00
|
|
|
# some required objectclasses
|
2007-10-11 12:19:42 -07:00
|
|
|
entry.setValues('objectClass', 'top', 'person', 'organizationalPerson',
|
2007-11-06 16:26:10 -05:00
|
|
|
'inetOrgPerson', 'inetUser', 'posixAccount', 'krbPrincipalAux', 'radiusprofile')
|
2007-10-11 12:19:42 -07:00
|
|
|
|
2007-08-06 10:05:53 -04:00
|
|
|
# fill in our new entry with everything sent by the user
|
|
|
|
|
for u in user:
|
2007-08-17 15:32:05 -07:00
|
|
|
entry.setValues(u, user[u])
|
2007-08-06 10:05:53 -04:00
|
|
|
|
2007-09-05 13:14:23 -04:00
|
|
|
conn = self.getConnection(opts)
|
2007-08-27 11:30:26 -07:00
|
|
|
try:
|
2007-09-05 13:14:23 -04:00
|
|
|
res = conn.addEntry(entry)
|
2007-08-27 11:30:26 -07:00
|
|
|
finally:
|
2007-09-05 13:14:23 -04:00
|
|
|
self.releaseConnection(conn)
|
2007-11-13 13:06:18 -05:00
|
|
|
return res
|
|
|
|
|
|
2007-11-14 00:04:19 -05:00
|
|
|
# radius support
|
|
|
|
|
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
# clients
|
|
|
|
|
def get_radius_client_by_ip_addr(self, ip_addr, container=None, sattrs=None, opts=None):
|
|
|
|
|
filter = radius_util.radius_client_filter(ip_addr)
|
|
|
|
|
basedn = radius_util.radius_clients_basedn(container, self.basedn)
|
2007-11-14 00:04:19 -05:00
|
|
|
return self.__get_sub_entry(basedn, filter, sattrs, opts)
|
|
|
|
|
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
def __radius_client_exists(self, ip_addr, container, opts):
|
|
|
|
|
filter = radius_util.radius_client_filter(ip_addr)
|
|
|
|
|
basedn = radius_util.radius_clients_basedn(container, self.basedn)
|
2007-11-13 20:05:02 -05:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
entry = self.__get_sub_entry(basedn, filter, ['dn','uid'], opts)
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
return True
|
2007-11-13 20:05:02 -05:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
return False
|
2007-11-13 20:05:02 -05:00
|
|
|
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
def add_radius_client (self, client, container=None, opts=None):
|
|
|
|
|
if container is None:
|
|
|
|
|
container = radius_util.clients_container
|
2007-11-13 13:06:18 -05:00
|
|
|
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
ip_addr = client['radiusClientIPAddress']
|
|
|
|
|
|
|
|
|
|
if self.__radius_client_exists(ip_addr, container, opts):
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
|
2007-11-13 13:06:18 -05:00
|
|
|
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
dn = radius_util.radius_client_dn(ip_addr, container, self.basedn)
|
2007-11-13 20:05:02 -05:00
|
|
|
entry = ipaserver.ipaldap.Entry(dn)
|
2007-11-13 13:06:18 -05:00
|
|
|
|
|
|
|
|
# some required objectclasses
|
|
|
|
|
entry.setValues('objectClass', 'top', 'radiusClientProfile')
|
|
|
|
|
|
|
|
|
|
# fill in our new entry with everything sent by the client
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
for attr in client:
|
|
|
|
|
entry.setValues(attr, client[attr])
|
2007-11-13 13:06:18 -05:00
|
|
|
|
|
|
|
|
conn = self.getConnection(opts)
|
|
|
|
|
try:
|
|
|
|
|
res = conn.addEntry(entry)
|
|
|
|
|
finally:
|
|
|
|
|
self.releaseConnection(conn)
|
2007-08-22 10:30:51 -07:00
|
|
|
return res
|
2007-08-06 10:05:53 -04:00
|
|
|
|
2007-11-14 00:04:19 -05:00
|
|
|
def update_radius_client(self, oldentry, newentry, opts=None):
|
|
|
|
|
return self.update_entry(oldentry, newentry, opts)
|
|
|
|
|
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
def delete_radius_client(self, ip_addr, container=None, opts=None):
|
|
|
|
|
client = self.get_radius_client_by_ip_addr(ip_addr, container, ['dn', 'cn'], opts)
|
2007-11-14 00:04:19 -05:00
|
|
|
if client is None:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
|
|
|
|
|
conn = self.getConnection(opts)
|
|
|
|
|
try:
|
|
|
|
|
res = conn.deleteEntry(client['dn'])
|
|
|
|
|
finally:
|
|
|
|
|
self.releaseConnection(conn)
|
|
|
|
|
return res
|
|
|
|
|
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
def find_radius_clients(self, ip_attrs, container=None, sattrs=None, searchlimit=0, timelimit=-1, opts=None):
|
2007-11-14 15:32:08 -05:00
|
|
|
def gen_filter(objectclass, attr, values):
|
|
|
|
|
'''Given ('myclass', 'myattr', [v1, v2]) returns
|
|
|
|
|
(&(objectclass=myclass)(|(myattr=v1)(myattr=v2)))
|
|
|
|
|
'''
|
|
|
|
|
# Don't use __safe_filter, prevents wildcarding
|
|
|
|
|
#attrs = ''.join(['(%s=%s)' % (attr, self.__safe_filter(val)) for val in values])
|
|
|
|
|
attrs = ''.join(['(%s=%s)' % (attr, val) for val in values])
|
|
|
|
|
filter = "(&(objectclass=%s)(|%s))" % (objectclass, attrs)
|
|
|
|
|
return filter
|
|
|
|
|
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
basedn = radius_util.radius_clients_basedn(container, self.basedn)
|
|
|
|
|
filter = gen_filter('radiusClientProfile', 'radiusClientIPAddress', ip_attrs)
|
2007-11-14 15:32:08 -05:00
|
|
|
conn = self.getConnection(opts)
|
|
|
|
|
try:
|
|
|
|
|
try:
|
|
|
|
|
results = conn.getListAsync(basedn, self.scope, filter, sattrs, 0, None, None, timelimit, searchlimit)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
results = [0]
|
|
|
|
|
finally:
|
|
|
|
|
self.releaseConnection(conn)
|
|
|
|
|
|
|
|
|
|
counter = results[0]
|
|
|
|
|
results = results[1:]
|
|
|
|
|
radius_clients = [counter]
|
|
|
|
|
for radius_client in results:
|
|
|
|
|
radius_clients.append(self.convert_entry(radius_client))
|
|
|
|
|
|
|
|
|
|
return radius_clients
|
|
|
|
|
|
Add radius profile implementations:
get_radius_profile_by_uid
add_radius_profile
update_radius_profile
delete_radius_profile
find_radius_profiles
Rewrite command line arg handling, now support pair entry, interactive
mode with auto completion, reading pairs from a file, better handling
of mandatory values, better help, long arg names now match attribute
name in pairs
Establish mappings for all attributes and names used in clients and
profiles
Add notion of containers to radius clients and profiles in LDAP
Move common code, variables, constants, and strings into the files
radius_client.py, radius_util.py, ipautil.py to eliminate redundant
elements which could get out of sync if modified and to provide access
to other code which might benefit from using these items in the
future.
Add utility functions:
format_list()
parse_key_value_pairs()
Add utility class:
AttributeValueCompleter
Unify attribute usage in radius ldap schema
2007-11-21 13:11:10 -05:00
|
|
|
# profiles
|
|
|
|
|
def get_radius_profile_by_uid(self, uid, user_profile=True, sattrs=None, opts=None):
|
|
|
|
|
if user_profile:
|
|
|
|
|
container = DefaultUserContainer
|
|
|
|
|
else:
|
|
|
|
|
container = radius_util.profiles_container
|
|
|
|
|
|
|
|
|
|
uid = self.__safe_filter(uid)
|
|
|
|
|
filter = radius_util.radius_profile_filter(uid)
|
|
|
|
|
basedn = radius_util.radius_profiles_basedn(container, self.basedn)
|
|
|
|
|
return self.__get_sub_entry(basedn, filter, sattrs, opts)
|
|
|
|
|
|
|
|
|
|
def __radius_profile_exists(self, uid, user_profile, opts):
|
|
|
|
|
if user_profile:
|
|
|
|
|
container = DefaultUserContainer
|
|
|
|
|
else:
|
|
|
|
|
container = radius_util.profiles_container
|
|
|
|
|
|
|
|
|
|
uid = self.__safe_filter(uid)
|
|
|
|
|
filter = radius_util.radius_profile_filter(uid)
|
|
|
|
|
basedn = radius_util.radius_profiles_basedn(container, self.basedn)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
entry = self.__get_sub_entry(basedn, filter, ['dn','uid'], opts)
|
|
|
|
|
return True
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def add_radius_profile (self, uid, user_profile=True, opts=None):
|
|
|
|
|
if self.__radius_profile_exists(profile['uid'], user_profile, opts):
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
|
|
|
|
|
|
|
|
|
|
if user_profile:
|
|
|
|
|
container = DefaultUserContainer
|
|
|
|
|
else:
|
|
|
|
|
container = radius_util.profiles_container
|
|
|
|
|
|
|
|
|
|
dn = radius_util.radius_profile_dn(uid, container, self.basedn)
|
|
|
|
|
entry = ipaserver.ipaldap.Entry(dn)
|
|
|
|
|
|
|
|
|
|
# some required objectclasses
|
|
|
|
|
entry.setValues('objectClass', 'top', 'radiusClientProfile')
|
|
|
|
|
|
|
|
|
|
# fill in our new entry with everything sent by the profile
|
|
|
|
|
for attr in profile:
|
|
|
|
|
entry.setValues(attr, profile[attr])
|
|
|
|
|
|
|
|
|
|
conn = self.getConnection(opts)
|
|
|
|
|
try:
|
|
|
|
|
res = conn.addEntry(entry)
|
|
|
|
|
finally:
|
|
|
|
|
self.releaseConnection(conn)
|
|
|
|
|
return res
|
|
|
|
|
|
|
|
|
|
def update_radius_profile(self, oldentry, newentry, opts=None):
|
|
|
|
|
return self.update_entry(oldentry, newentry, opts)
|
|
|
|
|
|
|
|
|
|
def delete_radius_profile(self, uid, user_profile, opts=None):
|
|
|
|
|
profile = self.get_radius_profile_by_uid(uid, user_profile, ['dn', 'cn'], opts)
|
|
|
|
|
if profile is None:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
|
|
|
|
|
conn = self.getConnection(opts)
|
|
|
|
|
try:
|
|
|
|
|
res = conn.deleteEntry(profile['dn'])
|
|
|
|
|
finally:
|
|
|
|
|
self.releaseConnection(conn)
|
|
|
|
|
return res
|
|
|
|
|
|
|
|
|
|
def find_radius_profiles(self, uids, user_profile=True, sattrs=None, searchlimit=0, timelimit=-1, opts=None):
|
|
|
|
|
def gen_filter(objectclass, attr, values):
|
|
|
|
|
'''Given ('myclass', 'myattr', [v1, v2]) returns
|
|
|
|
|
(&(objectclass=myclass)(|(myattr=v1)(myattr=v2)))
|
|
|
|
|
'''
|
|
|
|
|
# Don't use __safe_filter, prevents wildcarding
|
|
|
|
|
#attrs = ''.join(['(%s=%s)' % (attr, self.__safe_filter(val)) for val in values])
|
|
|
|
|
attrs = ''.join(['(%s=%s)' % (attr, val) for val in values])
|
|
|
|
|
filter = "(&(objectclass=%s)(|%s))" % (objectclass, attrs)
|
|
|
|
|
return filter
|
|
|
|
|
|
|
|
|
|
if user_profile:
|
|
|
|
|
container = DefaultUserContainer
|
|
|
|
|
else:
|
|
|
|
|
container = radius_util.profiles_container
|
|
|
|
|
|
|
|
|
|
uid = self.__safe_filter(uid)
|
|
|
|
|
filter = gen_filter('radiusClientProfile' 'uid', uids)
|
|
|
|
|
basedn="%s,%s" % (container, self.basedn)
|
|
|
|
|
conn = self.getConnection(opts)
|
|
|
|
|
try:
|
|
|
|
|
try:
|
|
|
|
|
results = conn.getListAsync(basedn, self.scope, filter, sattrs, 0, None, None, timelimit, searchlimit)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
results = [0]
|
|
|
|
|
finally:
|
|
|
|
|
self.releaseConnection(conn)
|
|
|
|
|
|
|
|
|
|
counter = results[0]
|
|
|
|
|
results = results[1:]
|
|
|
|
|
radius_profiles = [counter]
|
|
|
|
|
for radius_profile in results:
|
|
|
|
|
radius_profiles.append(self.convert_entry(radius_profile))
|
|
|
|
|
|
|
|
|
|
return radius_profiles
|
|
|
|
|
|
2007-08-06 10:05:53 -04:00
|
|
|
def get_add_schema (self):
|
|
|
|
|
"""Get the list of fields to be used when adding users in the GUI."""
|
|
|
|
|
|
|
|
|
|
# FIXME: this needs to be pulled from LDAP
|
|
|
|
|
fields = []
|
|
|
|
|
|
|
|
|
|
field1 = {
|
|
|
|
|
"name": "uid" ,
|
|
|
|
|
"label": "Login:",
|
|
|
|
|
"type": "text",
|
|
|
|
|
"validator": "text",
|
|
|
|
|
"required": "true"
|
|
|
|
|
}
|
|
|
|
|
fields.append(field1)
|
|
|
|
|
|
|
|
|
|
field1 = {
|
|
|
|
|
"name": "givenName" ,
|
|
|
|
|
"label": "First name:",
|
|
|
|
|
"type": "text",
|
|
|
|
|
"validator": "string",
|
|
|
|
|
"required": "true"
|
|
|
|
|
}
|
|
|
|
|
fields.append(field1)
|
|
|
|
|
|
|
|
|
|
field1 = {
|
|
|
|
|
"name": "sn" ,
|
|
|
|
|
"label": "Last name:",
|
|
|
|
|
"type": "text",
|
|
|
|
|
"validator": "string",
|
|
|
|
|
"required": "true"
|
|
|
|
|
}
|
|
|
|
|
fields.append(field1)
|
|
|
|
|
|
|
|
|
|
field1 = {
|
|
|
|
|
"name": "mail" ,
|
|
|
|
|
"label": "E-mail address:",
|
|
|
|
|
"type": "text",
|
|
|
|
|
"validator": "email",
|
2007-10-01 17:33:16 -04:00
|
|
|
"required": "false"
|
2007-08-06 10:05:53 -04:00
|
|
|
}
|
|
|
|
|
fields.append(field1)
|
|
|
|
|
|
|
|
|
|
return fields
|
|
|
|
|
|
2007-08-16 18:59:58 -04:00
|
|
|
def get_all_users (self, args=None, opts=None):
|
2007-08-06 10:05:53 -04:00
|
|
|
"""Return a list containing a User object for each
|
|
|
|
|
existing user.
|
|
|
|
|
"""
|
|
|
|
|
filter = "(objectclass=posixAccount)"
|
2007-08-22 10:30:51 -07:00
|
|
|
|
2007-09-05 13:14:23 -04:00
|
|
|
conn = self.getConnection(opts)
|
2007-08-27 11:30:26 -07:00
|
|
|
try:
|
2007-09-05 13:14:23 -04:00
|
|
|
all_users = conn.getList(self.basedn, self.scope, filter, None)
|
2007-08-27 11:30:26 -07:00
|
|
|
finally:
|
2007-09-05 13:14:23 -04:00
|
|
|
self.releaseConnection(conn)
|
2007-08-06 10:05:53 -04:00
|
|
|
|
|
|
|
|
users = []
|
|
|
|
|
for u in all_users:
|
|
|
|
|
users.append(self.convert_entry(u))
|
|
|
|
|
|
|
|
|
|
return users
|
2007-08-13 16:41:38 -04:00
|
|
|
|
2007-09-27 15:51:26 -07:00
|
|
|
def find_users (self, criteria, sattrs=None, searchlimit=0, timelimit=-1,
|
|
|
|
|
opts=None):
|
2007-08-28 16:01:07 -07:00
|
|
|
"""Returns a list: counter followed by the results.
|
|
|
|
|
If the results are truncated, counter will be set to -1."""
|
2007-09-25 11:25:48 -07:00
|
|
|
|
2007-10-01 17:33:16 -04:00
|
|
|
# TODO - retrieve from config
|
|
|
|
|
timelimit = 2
|
|
|
|
|
|
2007-08-27 11:30:26 -07:00
|
|
|
# Assume the list of fields to search will come from a central
|
|
|
|
|
# configuration repository. A good format for that would be
|
|
|
|
|
# a comma-separated list of fields
|
2007-09-25 11:25:48 -07:00
|
|
|
search_fields_conf_str = "uid,givenName,sn,telephoneNumber,ou,title"
|
2007-08-27 11:30:26 -07:00
|
|
|
search_fields = string.split(search_fields_conf_str, ",")
|
|
|
|
|
|
2007-08-24 15:42:56 -04:00
|
|
|
criteria = self.__safe_filter(criteria)
|
2007-08-27 11:30:26 -07:00
|
|
|
criteria_words = re.split(r'\s+', criteria)
|
|
|
|
|
criteria_words = filter(lambda value:value!="", criteria_words)
|
|
|
|
|
if len(criteria_words) == 0:
|
2007-09-07 11:07:59 -07:00
|
|
|
return [0]
|
2007-08-21 14:26:36 -07:00
|
|
|
|
2007-08-27 11:30:26 -07:00
|
|
|
(exact_match_filter, partial_match_filter) = self.__generate_match_filters(
|
|
|
|
|
search_fields, criteria_words)
|
|
|
|
|
|
2007-09-25 11:25:48 -07:00
|
|
|
#
|
|
|
|
|
# further constrain search to just the objectClass
|
|
|
|
|
# TODO - need to parameterize this into generate_match_filters,
|
|
|
|
|
# and work it into the field-specification search feature
|
|
|
|
|
#
|
|
|
|
|
exact_match_filter = "(&(objectClass=person)%s)" % exact_match_filter
|
|
|
|
|
partial_match_filter = "(&(objectClass=person)%s)" % partial_match_filter
|
|
|
|
|
|
2007-09-05 13:14:23 -04:00
|
|
|
conn = self.getConnection(opts)
|
2007-08-13 16:41:38 -04:00
|
|
|
try:
|
2007-08-27 11:30:26 -07:00
|
|
|
try:
|
2007-09-05 13:14:23 -04:00
|
|
|
exact_results = conn.getListAsync(self.basedn, self.scope,
|
2007-09-25 11:25:48 -07:00
|
|
|
exact_match_filter, sattrs, 0, None, None, timelimit,
|
|
|
|
|
searchlimit)
|
2007-08-27 11:30:26 -07:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
2007-08-28 16:01:07 -07:00
|
|
|
exact_results = [0]
|
2007-08-27 11:30:26 -07:00
|
|
|
|
|
|
|
|
try:
|
2007-09-05 13:14:23 -04:00
|
|
|
partial_results = conn.getListAsync(self.basedn, self.scope,
|
2007-09-25 11:25:48 -07:00
|
|
|
partial_match_filter, sattrs, 0, None, None, timelimit,
|
|
|
|
|
searchlimit)
|
2007-08-27 11:30:26 -07:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
2007-08-28 16:01:07 -07:00
|
|
|
partial_results = [0]
|
2007-08-27 11:30:26 -07:00
|
|
|
finally:
|
2007-09-05 13:14:23 -04:00
|
|
|
self.releaseConnection(conn)
|
2007-08-27 11:30:26 -07:00
|
|
|
|
2007-08-28 16:01:07 -07:00
|
|
|
exact_counter = exact_results[0]
|
|
|
|
|
partial_counter = partial_results[0]
|
|
|
|
|
|
|
|
|
|
exact_results = exact_results[1:]
|
|
|
|
|
partial_results = partial_results[1:]
|
|
|
|
|
|
2007-08-27 11:30:26 -07:00
|
|
|
# Remove exact matches from the partial_match list
|
|
|
|
|
exact_dns = set(map(lambda e: e.dn, exact_results))
|
|
|
|
|
partial_results = filter(lambda e: e.dn not in exact_dns,
|
|
|
|
|
partial_results)
|
2007-08-22 10:30:51 -07:00
|
|
|
|
2007-08-28 16:01:07 -07:00
|
|
|
if (exact_counter == -1) or (partial_counter == -1):
|
|
|
|
|
counter = -1
|
|
|
|
|
else:
|
|
|
|
|
counter = len(exact_results) + len(partial_results)
|
|
|
|
|
|
|
|
|
|
users = [counter]
|
2007-08-27 11:30:26 -07:00
|
|
|
for u in exact_results + partial_results:
|
2007-08-13 16:41:38 -04:00
|
|
|
users.append(self.convert_entry(u))
|
2007-08-27 11:30:26 -07:00
|
|
|
|
2007-08-13 16:41:38 -04:00
|
|
|
return users
|
2007-08-14 17:22:05 -04:00
|
|
|
|
2007-08-20 10:50:11 -07:00
|
|
|
def convert_scalar_values(self, orig_dict):
|
|
|
|
|
"""LDAP update dicts expect all values to be a list (except for dn).
|
|
|
|
|
This method converts single entries to a list."""
|
|
|
|
|
new_dict={}
|
|
|
|
|
for (k,v) in orig_dict.iteritems():
|
|
|
|
|
if not isinstance(v, list) and k != 'dn':
|
|
|
|
|
v = [v]
|
|
|
|
|
new_dict[k] = v
|
|
|
|
|
|
|
|
|
|
return new_dict
|
|
|
|
|
|
2007-10-22 10:09:39 -04:00
|
|
|
def update_user (self, oldentry, newentry, opts=None):
|
|
|
|
|
"""Thin wrapper around update_entry"""
|
|
|
|
|
return self.update_entry(oldentry, newentry, opts)
|
2007-08-17 10:03:33 -04:00
|
|
|
|
2007-08-23 09:44:00 -04:00
|
|
|
def mark_user_deleted (self, uid, opts=None):
|
2007-08-17 10:03:33 -04:00
|
|
|
"""Mark a user as inactive in LDAP. We aren't actually deleting
|
|
|
|
|
users here, just making it so they can't log in, etc."""
|
2007-08-23 09:44:00 -04:00
|
|
|
user = self.get_user_by_uid(uid, ['dn', 'uid', 'nsAccountlock'], opts)
|
2007-08-17 10:03:33 -04:00
|
|
|
|
|
|
|
|
# Are we doing an add or replace operation?
|
|
|
|
|
if user.has_key('nsaccountlock'):
|
2007-08-23 09:44:00 -04:00
|
|
|
if user['nsaccountlock'] == "true":
|
|
|
|
|
return "already marked as deleted"
|
2007-08-17 10:03:33 -04:00
|
|
|
has_key = True
|
|
|
|
|
else:
|
|
|
|
|
has_key = False
|
|
|
|
|
|
2007-09-05 13:14:23 -04:00
|
|
|
conn = self.getConnection(opts)
|
2007-08-27 11:30:26 -07:00
|
|
|
try:
|
2007-09-05 13:14:23 -04:00
|
|
|
res = conn.inactivateEntry(user['dn'], has_key)
|
2007-08-27 11:30:26 -07:00
|
|
|
finally:
|
2007-09-05 13:14:23 -04:00
|
|
|
self.releaseConnection(conn)
|
2007-08-22 10:30:51 -07:00
|
|
|
return res
|
2007-08-21 14:26:36 -07:00
|
|
|
|
2007-08-28 13:52:08 -04:00
|
|
|
def delete_user (self, uid, opts=None):
|
|
|
|
|
"""Delete a user. Not to be confused with inactivate_user. This
|
|
|
|
|
makes the entry go away completely.
|
|
|
|
|
|
|
|
|
|
uid is the uid of the user to delete
|
|
|
|
|
|
|
|
|
|
The memberOf plugin handles removing the user from any other
|
|
|
|
|
groups.
|
|
|
|
|
"""
|
2007-10-01 17:33:16 -04:00
|
|
|
user = self.get_user_by_uid(uid, ['dn', 'uid', 'objectclass'], opts)
|
|
|
|
|
if user is None:
|
2007-08-28 13:52:08 -04:00
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
|
2007-09-05 13:14:23 -04:00
|
|
|
conn = self.getConnection(opts)
|
|
|
|
|
try:
|
2007-10-01 17:33:16 -04:00
|
|
|
res = conn.deleteEntry(user['dn'])
|
2007-09-05 13:14:23 -04:00
|
|
|
finally:
|
|
|
|
|
self.releaseConnection(conn)
|
2007-08-28 13:52:08 -04:00
|
|
|
return res
|
|
|
|
|
|
2007-10-01 17:33:16 -04:00
|
|
|
def modifyPassword (self, principal, oldpass, newpass, opts=None):
|
2007-09-11 02:48:53 -04:00
|
|
|
"""Set/Reset a user's password
|
|
|
|
|
|
|
|
|
|
uid tells us who's password to change
|
|
|
|
|
oldpass is the old password (if available)
|
|
|
|
|
newpass is the new password
|
|
|
|
|
"""
|
2007-10-01 17:33:16 -04:00
|
|
|
user = self.get_user_by_principal(principal, ['krbprincipalname'], opts)
|
|
|
|
|
if user is None or user['krbprincipalname'] != principal:
|
2007-09-11 02:48:53 -04:00
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
|
|
|
|
|
conn = self.getConnection(opts)
|
|
|
|
|
try:
|
2007-10-01 17:33:16 -04:00
|
|
|
res = conn.modifyPassword(user['dn'], oldpass, newpass)
|
2007-09-11 02:48:53 -04:00
|
|
|
finally:
|
|
|
|
|
self.releaseConnection(conn)
|
|
|
|
|
return res
|
|
|
|
|
|
2007-08-24 15:42:56 -04:00
|
|
|
# Group support
|
|
|
|
|
|
|
|
|
|
def __is_group_unique(self, cn, opts):
|
|
|
|
|
"""Return 1 if the cn is unique in the tree, 0 otherwise."""
|
|
|
|
|
cn = self.__safe_filter(cn)
|
|
|
|
|
filter = "(&(cn=%s)(objectclass=posixGroup))" % cn
|
|
|
|
|
|
2007-08-28 13:52:08 -04:00
|
|
|
try:
|
2007-10-11 10:10:03 -07:00
|
|
|
entry = self.__get_sub_entry(self.basedn, filter, ['dn','cn'], opts)
|
2007-08-24 15:42:56 -04:00
|
|
|
return 0
|
2007-08-28 13:52:08 -04:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
2007-08-24 15:42:56 -04:00
|
|
|
return 1
|
|
|
|
|
|
2007-09-25 13:35:43 -07:00
|
|
|
def get_groups_by_member (self, member_dn, sattrs=None, opts=None):
|
|
|
|
|
"""Get a specific group's entry. Return as a dict of values.
|
|
|
|
|
Multi-valued fields are represented as lists.
|
|
|
|
|
"""
|
|
|
|
|
|
2007-09-27 16:07:05 -07:00
|
|
|
member_dn = self.__safe_filter(member_dn)
|
2007-09-25 13:35:43 -07:00
|
|
|
filter = "(&(objectClass=posixGroup)(uniqueMember=%s))" % member_dn
|
|
|
|
|
|
2007-09-25 15:44:49 -07:00
|
|
|
try:
|
|
|
|
|
return self.__get_list(self.basedn, filter, sattrs, opts)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
return []
|
2007-09-25 13:35:43 -07:00
|
|
|
|
2007-08-24 15:42:56 -04:00
|
|
|
def add_group (self, group, group_container=None, opts=None):
|
|
|
|
|
"""Add a group in LDAP. Takes as input a dict where the key is the
|
|
|
|
|
attribute name and the value is either a string or in the case
|
|
|
|
|
of a multi-valued field a list of values. group_container sets
|
|
|
|
|
where in the tree the group is placed."""
|
|
|
|
|
if group_container is None:
|
|
|
|
|
group_container = DefaultGroupContainer
|
|
|
|
|
|
|
|
|
|
if self.__is_group_unique(group['cn'], opts) == 0:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
|
|
|
|
|
|
2007-10-05 15:25:58 -07:00
|
|
|
dn="cn=%s,%s,%s" % (ldap.dn.escape_dn_chars(group['cn']),
|
|
|
|
|
group_container,self.basedn)
|
2007-08-24 15:42:56 -04:00
|
|
|
entry = ipaserver.ipaldap.Entry(dn)
|
|
|
|
|
|
|
|
|
|
# some required objectclasses
|
2007-10-11 12:19:42 -07:00
|
|
|
entry.setValues('objectClass', 'top', 'groupofuniquenames', 'posixGroup',
|
|
|
|
|
'inetUser')
|
2007-08-24 15:42:56 -04:00
|
|
|
|
|
|
|
|
# FIXME, need a gidNumber generator
|
|
|
|
|
if group.get('gidnumber') is None:
|
|
|
|
|
entry.setValues('gidNumber', '501')
|
|
|
|
|
|
|
|
|
|
# fill in our new entry with everything sent by the user
|
|
|
|
|
for g in group:
|
|
|
|
|
entry.setValues(g, group[g])
|
|
|
|
|
|
2007-09-05 13:14:23 -04:00
|
|
|
conn = self.getConnection(opts)
|
2007-08-27 11:30:26 -07:00
|
|
|
try:
|
2007-09-05 13:14:23 -04:00
|
|
|
res = conn.addEntry(entry)
|
2007-08-27 11:30:26 -07:00
|
|
|
finally:
|
2007-09-05 13:14:23 -04:00
|
|
|
self.releaseConnection(conn)
|
2007-08-24 15:42:56 -04:00
|
|
|
|
2007-09-27 15:51:26 -07:00
|
|
|
def find_groups (self, criteria, sattrs=None, searchlimit=0, timelimit=-1,
|
|
|
|
|
opts=None):
|
2007-08-24 15:42:56 -04:00
|
|
|
"""Return a list containing a User object for each
|
|
|
|
|
existing group that matches the criteria.
|
|
|
|
|
"""
|
2007-09-25 11:25:48 -07:00
|
|
|
|
2007-09-19 08:42:34 -07:00
|
|
|
# Assume the list of fields to search will come from a central
|
|
|
|
|
# configuration repository. A good format for that would be
|
|
|
|
|
# a comma-separated list of fields
|
|
|
|
|
search_fields_conf_str = "cn,description"
|
|
|
|
|
search_fields = string.split(search_fields_conf_str, ",")
|
|
|
|
|
|
2007-08-24 15:42:56 -04:00
|
|
|
criteria = self.__safe_filter(criteria)
|
2007-09-19 08:42:34 -07:00
|
|
|
criteria_words = re.split(r'\s+', criteria)
|
|
|
|
|
criteria_words = filter(lambda value:value!="", criteria_words)
|
|
|
|
|
if len(criteria_words) == 0:
|
|
|
|
|
return [0]
|
|
|
|
|
|
|
|
|
|
(exact_match_filter, partial_match_filter) = self.__generate_match_filters(
|
|
|
|
|
search_fields, criteria_words)
|
2007-08-24 15:42:56 -04:00
|
|
|
|
2007-09-19 08:42:34 -07:00
|
|
|
#
|
|
|
|
|
# further constrain search to just the objectClass
|
|
|
|
|
# TODO - need to parameterize this into generate_match_filters,
|
|
|
|
|
# and work it into the field-specification search feature
|
|
|
|
|
#
|
|
|
|
|
exact_match_filter = "(&(objectClass=posixGroup)%s)" % exact_match_filter
|
|
|
|
|
partial_match_filter = "(&(objectClass=posixGroup)%s)" % partial_match_filter
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# TODO - copy/paste from find_users. needs to be refactored
|
|
|
|
|
#
|
2007-09-05 13:14:23 -04:00
|
|
|
conn = self.getConnection(opts)
|
2007-08-24 15:42:56 -04:00
|
|
|
try:
|
2007-09-19 08:42:34 -07:00
|
|
|
try:
|
|
|
|
|
exact_results = conn.getListAsync(self.basedn, self.scope,
|
2007-09-25 11:25:48 -07:00
|
|
|
exact_match_filter, sattrs, 0, None, None, timelimit,
|
|
|
|
|
searchlimit)
|
2007-09-19 08:42:34 -07:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
exact_results = [0]
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
partial_results = conn.getListAsync(self.basedn, self.scope,
|
2007-09-25 11:25:48 -07:00
|
|
|
partial_match_filter, sattrs, 0, None, None, timelimit,
|
|
|
|
|
searchlimit)
|
2007-09-19 08:42:34 -07:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
partial_results = [0]
|
2007-08-27 11:30:26 -07:00
|
|
|
finally:
|
2007-09-05 13:14:23 -04:00
|
|
|
self.releaseConnection(conn)
|
2007-08-24 15:42:56 -04:00
|
|
|
|
2007-09-19 08:42:34 -07:00
|
|
|
exact_counter = exact_results[0]
|
|
|
|
|
partial_counter = partial_results[0]
|
|
|
|
|
|
|
|
|
|
exact_results = exact_results[1:]
|
|
|
|
|
partial_results = partial_results[1:]
|
|
|
|
|
|
|
|
|
|
# Remove exact matches from the partial_match list
|
|
|
|
|
exact_dns = set(map(lambda e: e.dn, exact_results))
|
|
|
|
|
partial_results = filter(lambda e: e.dn not in exact_dns,
|
|
|
|
|
partial_results)
|
|
|
|
|
|
|
|
|
|
if (exact_counter == -1) or (partial_counter == -1):
|
|
|
|
|
counter = -1
|
|
|
|
|
else:
|
|
|
|
|
counter = len(exact_results) + len(partial_results)
|
|
|
|
|
|
|
|
|
|
groups = [counter]
|
|
|
|
|
for u in exact_results + partial_results:
|
2007-08-24 15:42:56 -04:00
|
|
|
groups.append(self.convert_entry(u))
|
2007-09-19 08:42:34 -07:00
|
|
|
|
2007-08-24 15:42:56 -04:00
|
|
|
return groups
|
|
|
|
|
|
2007-10-02 16:56:51 -04:00
|
|
|
def add_member_to_group(self, member_dn, group_dn, opts=None):
|
2007-09-26 15:47:34 -07:00
|
|
|
"""Add a member to an existing group.
|
2007-08-24 15:42:56 -04:00
|
|
|
"""
|
|
|
|
|
|
2007-10-09 09:26:16 -07:00
|
|
|
old_group = self.get_entry_by_dn(group_dn, None, opts)
|
2007-08-24 15:42:56 -04:00
|
|
|
if old_group is None:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
new_group = copy.deepcopy(old_group)
|
|
|
|
|
|
2007-09-26 15:47:34 -07:00
|
|
|
# check to make sure member_dn exists
|
2007-10-11 10:10:03 -07:00
|
|
|
member_entry = self.__get_base_entry(member_dn, "(objectClass=*)", ['dn','uid'], opts)
|
2007-08-24 15:42:56 -04:00
|
|
|
|
|
|
|
|
if new_group.get('uniquemember') is not None:
|
|
|
|
|
if ((isinstance(new_group.get('uniquemember'), str)) or (isinstance(new_group.get('uniquemember'), unicode))):
|
|
|
|
|
new_group['uniquemember'] = [new_group['uniquemember']]
|
2007-09-26 15:47:34 -07:00
|
|
|
new_group['uniquemember'].append(member_dn)
|
2007-08-24 15:42:56 -04:00
|
|
|
else:
|
2007-09-26 15:47:34 -07:00
|
|
|
new_group['uniquemember'] = member_dn
|
2007-08-24 15:42:56 -04:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
ret = self.__update_entry(old_group, new_group, opts)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST):
|
|
|
|
|
raise
|
|
|
|
|
return ret
|
|
|
|
|
|
2007-10-02 16:56:51 -04:00
|
|
|
def add_members_to_group(self, member_dns, group_dn, opts=None):
|
2007-09-26 15:47:34 -07:00
|
|
|
"""Given a list of dn's, add them to the group cn denoted by group
|
|
|
|
|
Returns a list of the member_dns that were not added to the group.
|
2007-08-24 15:42:56 -04:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
failed = []
|
|
|
|
|
|
2007-09-26 15:47:34 -07:00
|
|
|
if (isinstance(member_dns, str)):
|
|
|
|
|
member_dns = [member_dns]
|
2007-08-24 15:42:56 -04:00
|
|
|
|
2007-09-26 15:47:34 -07:00
|
|
|
for member_dn in member_dns:
|
2007-08-24 15:42:56 -04:00
|
|
|
try:
|
2007-10-02 16:56:51 -04:00
|
|
|
self.add_member_to_group(member_dn, group_dn, opts)
|
2007-08-24 15:42:56 -04:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST):
|
|
|
|
|
# User is already in the group
|
2007-09-26 15:47:34 -07:00
|
|
|
failed.append(member_dn)
|
2007-09-19 13:43:52 -07:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
2007-08-24 15:42:56 -04:00
|
|
|
# User or the group does not exist
|
2007-09-26 15:47:34 -07:00
|
|
|
failed.append(member_dn)
|
2007-08-24 15:42:56 -04:00
|
|
|
|
|
|
|
|
return failed
|
|
|
|
|
|
2007-10-02 16:56:51 -04:00
|
|
|
def remove_member_from_group(self, member_dn, group_dn, opts=None):
|
2007-09-26 15:47:34 -07:00
|
|
|
"""Remove a member_dn from an existing group.
|
2007-08-24 15:42:56 -04:00
|
|
|
"""
|
|
|
|
|
|
2007-10-09 09:26:16 -07:00
|
|
|
old_group = self.get_entry_by_dn(group_dn, None, opts)
|
2007-08-24 15:42:56 -04:00
|
|
|
if old_group is None:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
new_group = copy.deepcopy(old_group)
|
|
|
|
|
|
|
|
|
|
if new_group.get('uniquemember') is not None:
|
|
|
|
|
if ((isinstance(new_group.get('uniquemember'), str)) or (isinstance(new_group.get('uniquemember'), unicode))):
|
|
|
|
|
new_group['uniquemember'] = [new_group['uniquemember']]
|
|
|
|
|
try:
|
2007-09-26 15:47:34 -07:00
|
|
|
new_group['uniquemember'].remove(member_dn)
|
2007-08-24 15:42:56 -04:00
|
|
|
except ValueError:
|
2007-09-26 15:47:34 -07:00
|
|
|
# member is not in the group
|
2007-08-24 15:42:56 -04:00
|
|
|
# FIXME: raise more specific error?
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
else:
|
|
|
|
|
# Nothing to do if the group has no members
|
|
|
|
|
# FIXME raise SOMETHING?
|
|
|
|
|
return "Success"
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
ret = self.__update_entry(old_group, new_group, opts)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST):
|
|
|
|
|
raise
|
|
|
|
|
return ret
|
|
|
|
|
|
2007-10-02 16:56:51 -04:00
|
|
|
def remove_members_from_group(self, member_dns, group_dn, opts=None):
|
2007-09-26 15:47:34 -07:00
|
|
|
"""Given a list of member dn's remove them from the group.
|
|
|
|
|
Returns a list of the members not removed from the group.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
failed = []
|
|
|
|
|
|
|
|
|
|
if (isinstance(member_dns, str)):
|
|
|
|
|
member_dns = [member_dns]
|
|
|
|
|
|
|
|
|
|
for member_dn in member_dns:
|
|
|
|
|
try:
|
2007-10-02 16:56:51 -04:00
|
|
|
self.remove_member_from_group(member_dn, group_dn, opts)
|
2007-09-26 15:47:34 -07:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST):
|
|
|
|
|
# member is not in the group
|
|
|
|
|
failed.append(member_dn)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
# member_dn or the group does not exist
|
|
|
|
|
failed.append(member_dn)
|
|
|
|
|
|
|
|
|
|
return failed
|
|
|
|
|
|
2007-10-02 16:56:51 -04:00
|
|
|
def add_user_to_group(self, user_uid, group_dn, opts=None):
|
2007-09-26 15:47:34 -07:00
|
|
|
"""Add a user to an existing group.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
user = self.get_user_by_uid(user_uid, ['dn', 'uid', 'objectclass'], opts)
|
|
|
|
|
if user is None:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
|
2007-10-02 16:56:51 -04:00
|
|
|
return self.add_member_to_group(user['dn'], group_dn, opts)
|
2007-09-26 15:47:34 -07:00
|
|
|
|
2007-10-02 16:56:51 -04:00
|
|
|
def add_users_to_group(self, user_uids, group_dn, opts=None):
|
2007-09-26 15:47:34 -07:00
|
|
|
"""Given a list of user uid's add them to the group cn denoted by group
|
|
|
|
|
Returns a list of the users were not added to the group.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
failed = []
|
|
|
|
|
|
|
|
|
|
if (isinstance(user_uids, str)):
|
|
|
|
|
user_uids = [user_uids]
|
|
|
|
|
|
|
|
|
|
for user_uid in user_uids:
|
|
|
|
|
try:
|
2007-10-02 16:56:51 -04:00
|
|
|
self.add_user_to_group(user_uid, group_dn, opts)
|
2007-09-26 15:47:34 -07:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST):
|
|
|
|
|
# User is already in the group
|
|
|
|
|
failed.append(user_uid)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
# User or the group does not exist
|
|
|
|
|
failed.append(user_uid)
|
|
|
|
|
|
|
|
|
|
return failed
|
|
|
|
|
|
2007-10-02 16:56:51 -04:00
|
|
|
def remove_user_from_group(self, user_uid, group_dn, opts=None):
|
2007-09-26 15:47:34 -07:00
|
|
|
"""Remove a user from an existing group.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
user = self.get_user_by_uid(user_uid, ['dn', 'uid', 'objectclass'], opts)
|
|
|
|
|
if user is None:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
|
2007-10-02 16:56:51 -04:00
|
|
|
return self.remove_member_from_group(user['dn'], group_dn, opts)
|
2007-09-26 15:47:34 -07:00
|
|
|
|
2007-10-02 16:56:51 -04:00
|
|
|
def remove_users_from_group(self, user_uids, group_dn, opts=None):
|
2007-09-26 15:47:34 -07:00
|
|
|
"""Given a list of user uid's remove them from the group
|
|
|
|
|
Returns a list of the user uids not removed from the group.
|
2007-08-24 15:42:56 -04:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
failed = []
|
|
|
|
|
|
2007-09-26 15:47:34 -07:00
|
|
|
if (isinstance(user_uids, str)):
|
|
|
|
|
user_uids = [user_uids]
|
2007-08-24 15:42:56 -04:00
|
|
|
|
2007-09-26 15:47:34 -07:00
|
|
|
for user_uid in user_uids:
|
2007-08-24 15:42:56 -04:00
|
|
|
try:
|
2007-10-02 16:56:51 -04:00
|
|
|
self.remove_user_from_group(user_uid, group_dn, opts)
|
2007-08-24 15:42:56 -04:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST):
|
|
|
|
|
# User is not in the group
|
2007-09-26 15:47:34 -07:00
|
|
|
failed.append(user_uid)
|
2007-09-19 13:43:52 -07:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
2007-08-24 15:42:56 -04:00
|
|
|
# User or the group does not exist
|
2007-09-26 15:47:34 -07:00
|
|
|
failed.append(user_uid)
|
2007-08-24 15:42:56 -04:00
|
|
|
|
|
|
|
|
return failed
|
|
|
|
|
|
2007-09-28 16:01:42 -07:00
|
|
|
def add_groups_to_user(self, group_dns, user_dn, opts=None):
|
|
|
|
|
"""Given a list of group dn's add them to the user.
|
|
|
|
|
|
|
|
|
|
Returns a list of the group dns that were not added.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
failed = []
|
|
|
|
|
|
|
|
|
|
if (isinstance(group_dns, str)):
|
|
|
|
|
group_dns = [group_dns]
|
|
|
|
|
|
|
|
|
|
for group_dn in group_dns:
|
|
|
|
|
try:
|
2007-10-02 17:26:09 -04:00
|
|
|
self.add_member_to_group(user_dn, group_dn, opts)
|
2007-09-28 16:01:42 -07:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST):
|
|
|
|
|
# User is already in the group
|
|
|
|
|
failed.append(group_dn)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
# User or the group does not exist
|
|
|
|
|
failed.append(group_dn)
|
|
|
|
|
|
|
|
|
|
return failed
|
|
|
|
|
|
|
|
|
|
def remove_groups_from_user(self, group_dns, user_dn, opts=None):
|
|
|
|
|
"""Given a list of group dn's remove them from the user.
|
|
|
|
|
|
|
|
|
|
Returns a list of the group dns that were not removed.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
failed = []
|
|
|
|
|
|
|
|
|
|
if (isinstance(group_dns, str)):
|
|
|
|
|
group_dns = [group_dns]
|
|
|
|
|
|
|
|
|
|
for group_dn in group_dns:
|
|
|
|
|
try:
|
2007-10-02 17:26:09 -04:00
|
|
|
self.remove_member_from_group(user_dn, group_dn, opts)
|
2007-09-28 16:01:42 -07:00
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST):
|
|
|
|
|
# User is not in the group
|
|
|
|
|
failed.append(group_dn)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
# User or the group does not exist
|
|
|
|
|
failed.append(group_dn)
|
|
|
|
|
|
|
|
|
|
return failed
|
|
|
|
|
|
2007-10-22 10:09:39 -04:00
|
|
|
def update_group (self, oldentry, newentry, opts=None):
|
|
|
|
|
"""Thin wrapper around update_entry"""
|
|
|
|
|
return self.update_entry(oldentry, newentry, opts)
|
2007-08-21 14:26:36 -07:00
|
|
|
|
2007-10-02 16:56:51 -04:00
|
|
|
def delete_group (self, group_dn, opts=None):
|
2007-08-28 13:52:08 -04:00
|
|
|
"""Delete a group
|
2007-10-02 16:56:51 -04:00
|
|
|
group_dn is the DN of the group to delete
|
2007-08-28 13:52:08 -04:00
|
|
|
|
|
|
|
|
The memberOf plugin handles removing the group from any other
|
|
|
|
|
groups.
|
|
|
|
|
"""
|
2007-10-09 09:26:16 -07:00
|
|
|
group = self.get_entry_by_dn(group_dn, ['dn', 'cn'], opts)
|
2007-10-23 16:46:50 -07:00
|
|
|
if group is None:
|
2007-08-28 13:52:08 -04:00
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
|
2007-09-05 13:14:23 -04:00
|
|
|
conn = self.getConnection(opts)
|
|
|
|
|
try:
|
2007-10-23 16:46:50 -07:00
|
|
|
res = conn.deleteEntry(group_dn)
|
2007-09-05 13:14:23 -04:00
|
|
|
finally:
|
|
|
|
|
self.releaseConnection(conn)
|
2007-08-28 13:52:08 -04:00
|
|
|
return res
|
|
|
|
|
|
|
|
|
|
def add_group_to_group(self, group, tgroup, opts=None):
|
|
|
|
|
"""Add a user to an existing group.
|
2007-10-02 16:56:51 -04:00
|
|
|
group is a DN of the group to add
|
|
|
|
|
tgroup is the DN of the target group to be added to
|
2007-08-28 13:52:08 -04:00
|
|
|
"""
|
|
|
|
|
|
2007-10-09 09:26:16 -07:00
|
|
|
old_group = self.get_entry_by_dn(tgroup, None, opts)
|
2007-08-28 13:52:08 -04:00
|
|
|
if old_group is None:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
new_group = copy.deepcopy(old_group)
|
|
|
|
|
|
2007-10-09 09:26:16 -07:00
|
|
|
group_dn = self.get_entry_by_dn(group, ['dn', 'cn', 'objectclass'], opts)
|
2007-08-28 13:52:08 -04:00
|
|
|
if group_dn is None:
|
|
|
|
|
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
|
|
|
|
|
|
|
|
|
|
if new_group.get('uniquemember') is not None:
|
|
|
|
|
if ((isinstance(new_group.get('uniquemember'), str)) or (isinstance(new_group.get('uniquemember'), unicode))):
|
|
|
|
|
new_group['uniquemember'] = [new_group['uniquemember']]
|
|
|
|
|
new_group['uniquemember'].append(group_dn['dn'])
|
|
|
|
|
else:
|
|
|
|
|
new_group['uniquemember'] = group_dn['dn']
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
ret = self.__update_entry(old_group, new_group, opts)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_EMPTY_MODLIST):
|
|
|
|
|
raise
|
|
|
|
|
return ret
|
|
|
|
|
|
2007-10-22 17:06:52 -04:00
|
|
|
def attrs_to_labels(self, attr_list, opts=None):
|
|
|
|
|
"""Take a list of LDAP attributes and convert them to more friendly
|
|
|
|
|
labels."""
|
|
|
|
|
label_list = {}
|
|
|
|
|
|
|
|
|
|
for a in attr_list:
|
|
|
|
|
label_list[a] = attrs.attr_label_list.get(a,a)
|
|
|
|
|
|
|
|
|
|
return label_list
|
|
|
|
|
|
|
|
|
|
def group_members(self, groupdn, attr_list, opts=None):
|
2007-10-30 15:07:02 -04:00
|
|
|
"""Do a memberOf search of groupdn and return the attributes in
|
|
|
|
|
attr_list (an empty list returns everything)."""
|
|
|
|
|
|
|
|
|
|
# TODO - retrieve from config
|
|
|
|
|
timelimit = 2
|
|
|
|
|
|
|
|
|
|
searchlimit = 0
|
|
|
|
|
|
|
|
|
|
groupdn = self.__safe_filter(groupdn)
|
|
|
|
|
filter = "(memberOf=%s)" % groupdn
|
|
|
|
|
|
|
|
|
|
conn = self.getConnection(opts)
|
|
|
|
|
try:
|
|
|
|
|
results = conn.getListAsync(self.basedn, self.scope,
|
|
|
|
|
filter, attr_list, 0, None, None, timelimit,
|
|
|
|
|
searchlimit)
|
|
|
|
|
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
|
|
|
|
results = [0]
|
|
|
|
|
finally:
|
|
|
|
|
self.releaseConnection(conn)
|
|
|
|
|
|
|
|
|
|
counter = results[0]
|
|
|
|
|
results = results[1:]
|
|
|
|
|
|
|
|
|
|
entries = [counter]
|
|
|
|
|
for e in results:
|
|
|
|
|
entries.append(self.convert_entry(e))
|
|
|
|
|
|
|
|
|
|
return entries
|
|
|
|
|
|
2007-11-14 15:32:08 -05:00
|
|
|
|
2007-08-21 14:26:36 -07:00
|
|
|
def ldap_search_escape(match):
|
|
|
|
|
"""Escapes out nasty characters from the ldap search.
|
|
|
|
|
See RFC 2254."""
|
|
|
|
|
value = match.group()
|
|
|
|
|
if (len(value) != 1):
|
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
if value == "(":
|
|
|
|
|
return "\\28"
|
|
|
|
|
elif value == ")":
|
|
|
|
|
return "\\29"
|
|
|
|
|
elif value == "\\":
|
|
|
|
|
return "\\5c"
|
2007-08-27 11:30:26 -07:00
|
|
|
elif value == "*":
|
|
|
|
|
# drop '*' from input. search performs its own wildcarding
|
|
|
|
|
return ""
|
2007-10-05 15:25:58 -07:00
|
|
|
elif value =='\x00':
|
|
|
|
|
return r'\00'
|
2007-08-21 14:26:36 -07:00
|
|
|
else:
|
|
|
|
|
return value
|