mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Merge.
This commit is contained in:
@@ -330,6 +330,32 @@ class IPAServer:
|
||||
|
||||
return (exact_match_filter, partial_match_filter)
|
||||
|
||||
def __get_schema(self, opts=None):
|
||||
"""Retrieves the current LDAP schema from the LDAP server."""
|
||||
|
||||
schema_entry = self.__get_base_entry("", "objectclass=*", ['dn','subschemasubentry'], opts)
|
||||
schema_cn = schema_entry.get('subschemasubentry')
|
||||
schema = self.__get_base_entry(schema_cn, "objectclass=*", ['*'], opts)
|
||||
|
||||
return schema
|
||||
|
||||
def __get_objectclasses(self, opts=None):
|
||||
"""Returns a list of available objectclasses that the LDAP
|
||||
server supports. This parses out the syntax, attributes, etc
|
||||
and JUST returns a lower-case list of the names."""
|
||||
|
||||
schema = self.__get_schema(opts)
|
||||
|
||||
objectclasses = schema.get('objectclasses')
|
||||
|
||||
# Convert this list into something more readable
|
||||
result = []
|
||||
for i in range(len(objectclasses)):
|
||||
oc = objectclasses[i].lower().split(" ")
|
||||
result.append(oc[3].replace("'",""))
|
||||
|
||||
return result
|
||||
|
||||
# Higher-level API
|
||||
|
||||
def get_aci_entry(self, sattrs, opts=None):
|
||||
@@ -424,6 +450,19 @@ class IPAServer:
|
||||
if self.__is_user_unique(user['uid'], opts) == 0:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
|
||||
|
||||
# dn is set here, not by the user
|
||||
try:
|
||||
del user['dn']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# No need to set empty fields, and they can cause issues when they
|
||||
# get to LDAP, like:
|
||||
# TypeError: ('expected a string in the list', None)
|
||||
for k in user.keys():
|
||||
if not user[k] or len(user[k]) == 0 or (len(user[k]) == 1 and '' in user[k]):
|
||||
del user[k]
|
||||
|
||||
dn="uid=%s,%s,%s" % (ldap.dn.escape_dn_chars(user['uid']),
|
||||
user_container,self.basedn)
|
||||
entry = ipaserver.ipaldap.Entry(dn)
|
||||
@@ -468,8 +507,7 @@ class IPAServer:
|
||||
del user['gn']
|
||||
|
||||
# some required objectclasses
|
||||
entry.setValues('objectClass', 'top', 'person', 'organizationalPerson',
|
||||
'inetOrgPerson', 'inetUser', 'posixAccount', 'krbPrincipalAux', 'radiusprofile')
|
||||
entry.setValues('objectClass', (config.get('ipauserobjectclasses')))
|
||||
|
||||
# fill in our new entry with everything sent by the user
|
||||
for u in user:
|
||||
@@ -477,8 +515,16 @@ class IPAServer:
|
||||
|
||||
conn = self.getConnection(opts)
|
||||
try:
|
||||
res = conn.addEntry(entry)
|
||||
self.add_user_to_group(user.get('uid'), group_dn, opts)
|
||||
try:
|
||||
res = conn.addEntry(entry)
|
||||
except TypeError, e:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, "There is a problem with one of the data types.")
|
||||
except Exception, e:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, e)
|
||||
try:
|
||||
self.add_user_to_group(user.get('uid'), group_dn, opts)
|
||||
except Exception, e:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, "The user was created but adding to group %s failed" % group_dn)
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
return res
|
||||
@@ -867,6 +913,12 @@ class IPAServer:
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
|
||||
# Get our configuration
|
||||
config = self.get_ipa_config(opts)
|
||||
|
||||
# Make sure we have the latest object classes
|
||||
newentry['objectclass'] = uniq_list(newentry.get('objectclass') + config.get('ipauserobjectclasses'))
|
||||
|
||||
try:
|
||||
rv = self.update_entry(oldentry, newentry, opts)
|
||||
return rv
|
||||
@@ -1026,13 +1078,15 @@ class IPAServer:
|
||||
if self.__is_group_unique(group['cn'], opts) == 0:
|
||||
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
|
||||
|
||||
# Get our configuration
|
||||
config = self.get_ipa_config(opts)
|
||||
|
||||
dn="cn=%s,%s,%s" % (ldap.dn.escape_dn_chars(group['cn']),
|
||||
group_container,self.basedn)
|
||||
entry = ipaserver.ipaldap.Entry(dn)
|
||||
|
||||
# some required objectclasses
|
||||
entry.setValues('objectClass', 'top', 'groupofnames', 'posixGroup',
|
||||
'inetUser')
|
||||
entry.setValues('objectClass', (config.get('ipagroupobjectclasses')))
|
||||
|
||||
# No need to explicitly set gidNumber. The dna_plugin will do this
|
||||
# for us if the value isn't provided by the user.
|
||||
@@ -1357,23 +1411,32 @@ class IPAServer:
|
||||
try:
|
||||
res = conn.updateRDN(oldentry.get('dn'), "cn=" + newcn[0])
|
||||
newdn = oldentry.get('dn')
|
||||
newcn = newentry.get('cn')
|
||||
if isinstance(newcn, str):
|
||||
newcn = [newcn]
|
||||
|
||||
# Ick. Need to find the exact cn used in the old DN so we'll
|
||||
# walk the list of cns and skip the obviously bad ones:
|
||||
for c in oldentry.get('dn').split("cn="):
|
||||
if c and c != "groups" and not c.startswith("accounts"):
|
||||
newdn = newdn.replace("cn=%s" % c, "uid=%s" % newentry.get('cn')[0])
|
||||
newdn = newdn.replace("cn=%s" % c, "cn=%s," % newcn[0])
|
||||
break
|
||||
|
||||
# Now fix up the dns and cns so they aren't seen as having
|
||||
# changed.
|
||||
oldentry['dn'] = newdn
|
||||
newentry['dn'] = newdn
|
||||
oldentry['cn'] = newentry['cn']
|
||||
oldentry['cn'] = newentry.get('cn')
|
||||
newrdn = 1
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
|
||||
# Get our configuration
|
||||
config = self.get_ipa_config(opts)
|
||||
|
||||
# Make sure we have the latest object classes
|
||||
newentry['objectclass'] = uniq_list(newentry.get('objectclass') + config.get('ipagroupobjectclasses'))
|
||||
|
||||
try:
|
||||
rv = self.update_entry(oldentry, newentry, opts)
|
||||
return rv
|
||||
@@ -1527,7 +1590,76 @@ class IPAServer:
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
return res
|
||||
|
||||
|
||||
def find_service_principal(self, criteria, sattrs, searchlimit=-1,
|
||||
timelimit=-1, opts=None):
|
||||
"""Returns a list: counter followed by the results.
|
||||
If the results are truncated, counter will be set to -1."""
|
||||
|
||||
config = self.get_ipa_config(opts)
|
||||
if timelimit < 0:
|
||||
timelimit = float(config.get('ipasearchtimelimit'))
|
||||
if searchlimit < 0:
|
||||
searchlimit = float(config.get('ipasearchrecordslimit'))
|
||||
|
||||
search_fields = ["krbprincipalname"]
|
||||
|
||||
criteria = self.__safe_filter(criteria)
|
||||
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)
|
||||
|
||||
#
|
||||
# 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=krbPrincipalAux)(!(objectClass=person))(!(krbprincipalname=kadmin/*))%s)" % exact_match_filter
|
||||
partial_match_filter = "(&(objectclass=krbPrincipalAux)(!(objectClass=person))(!(krbprincipalname=kadmin/*))%s)" % partial_match_filter
|
||||
|
||||
conn = self.getConnection(opts)
|
||||
try:
|
||||
try:
|
||||
exact_results = conn.getListAsync(self.basedn, self.scope,
|
||||
exact_match_filter, sattrs, 0, None, None, timelimit,
|
||||
searchlimit)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||
exact_results = [0]
|
||||
|
||||
try:
|
||||
partial_results = conn.getListAsync(self.basedn, self.scope,
|
||||
partial_match_filter, sattrs, 0, None, None, timelimit,
|
||||
searchlimit)
|
||||
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
|
||||
partial_results = [0]
|
||||
finally:
|
||||
self.releaseConnection(conn)
|
||||
|
||||
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)
|
||||
|
||||
entries = [counter]
|
||||
for e in exact_results + partial_results:
|
||||
entries.append(self.convert_entry(e))
|
||||
|
||||
return entries
|
||||
|
||||
def get_keytab(self, name, opts=None):
|
||||
"""get a keytab"""
|
||||
@@ -1592,6 +1724,19 @@ class IPAServer:
|
||||
except:
|
||||
raise
|
||||
|
||||
# Run through the list of User and Group object classes to make
|
||||
# sure they are all valid. This doesn't handle dependencies but it
|
||||
# will at least catch typos.
|
||||
classes = self.__get_objectclasses(opts)
|
||||
oc = newconfig['ipauserobjectclasses']
|
||||
for i in range(len(oc)):
|
||||
if not oc[i].lower() in classes:
|
||||
raise ipaerror.gen_exception(ipaerror.CONFIG_INVALID_OC)
|
||||
oc = newconfig['ipagroupobjectclasses']
|
||||
for i in range(len(oc)):
|
||||
if not oc[i].lower() in classes:
|
||||
raise ipaerror.gen_exception(ipaerror.CONFIG_INVALID_OC)
|
||||
|
||||
return self.update_entry(oldconfig, newconfig, opts)
|
||||
|
||||
def get_password_policy(self, opts=None):
|
||||
@@ -1654,3 +1799,8 @@ def ldap_search_escape(match):
|
||||
return r'\00'
|
||||
else:
|
||||
return value
|
||||
|
||||
def uniq_list(x):
|
||||
"""Return a unique list, preserving order and ignoring case"""
|
||||
set = {}
|
||||
return [set.setdefault(e,e) for e in x if e.lower() not in set]
|
||||
|
||||
Reference in New Issue
Block a user