ipaldap: allow GetEffectiveRights on individual operations

Allow caller to specify that the GetEffectiveRights server control
should be used on a per-operation basis.  Also update
ldap2.get_effective_rights to use this new API.

Part of: https://pagure.io/freeipa/issue/6609

Reviewed-By: Christian Heimes <cheimes@redhat.com>
This commit is contained in:
Fraser Tweedale
2018-02-09 08:57:41 +01:00
committed by Christian Heimes
parent ece17cef17
commit 4daac52ddd
2 changed files with 36 additions and 33 deletions
+35 -16
View File
@@ -40,7 +40,7 @@ from cryptography import x509 as crypto_x509
import ldap import ldap
import ldap.sasl import ldap.sasl
import ldap.filter import ldap.filter
from ldap.controls import SimplePagedResultsControl from ldap.controls import SimplePagedResultsControl, GetEffectiveRightsControl
import six import six
# pylint: disable=ipa-forbidden-import # pylint: disable=ipa-forbidden-import
@@ -1318,7 +1318,7 @@ class LDAPClient(object):
return cls.combine_filters(flts, rules) return cls.combine_filters(flts, rules)
def get_entries(self, base_dn, scope=ldap.SCOPE_SUBTREE, filter=None, def get_entries(self, base_dn, scope=ldap.SCOPE_SUBTREE, filter=None,
attrs_list=None, **kwargs): attrs_list=None, get_effective_rights=False, **kwargs):
"""Return a list of matching entries. """Return a list of matching entries.
:raises: errors.LimitsExceeded if the list is truncated by the server :raises: errors.LimitsExceeded if the list is truncated by the server
@@ -1329,11 +1329,13 @@ class LDAPClient(object):
:param scope: search scope, see LDAP docs (default ldap2.SCOPE_SUBTREE) :param scope: search scope, see LDAP docs (default ldap2.SCOPE_SUBTREE)
:param filter: LDAP filter to apply :param filter: LDAP filter to apply
:param attrs_list: ist of attributes to return, all if None (default) :param attrs_list: ist of attributes to return, all if None (default)
:param get_effective_rights: use GetEffectiveRights control
:param kwargs: additional keyword arguments. See find_entries method :param kwargs: additional keyword arguments. See find_entries method
for their description. for their description.
""" """
entries, truncated = self.find_entries( entries, truncated = self.find_entries(
base_dn=base_dn, scope=scope, filter=filter, attrs_list=attrs_list, base_dn=base_dn, scope=scope, filter=filter, attrs_list=attrs_list,
get_effective_rights=get_effective_rights,
**kwargs) **kwargs)
try: try:
self.handle_truncated_result(truncated) self.handle_truncated_result(truncated)
@@ -1346,9 +1348,10 @@ class LDAPClient(object):
return entries return entries
def find_entries(self, filter=None, attrs_list=None, base_dn=None, def find_entries(
scope=ldap.SCOPE_SUBTREE, time_limit=None, self, filter=None, attrs_list=None, base_dn=None,
size_limit=None, paged_search=False): scope=ldap.SCOPE_SUBTREE, time_limit=None, size_limit=None,
paged_search=False, get_effective_rights=False):
""" """
Return a list of entries and indication of whether the results were Return a list of entries and indication of whether the results were
truncated ([(dn, entry_attrs)], truncated) matching specified search truncated ([(dn, entry_attrs)], truncated) matching specified search
@@ -1356,13 +1359,16 @@ class LDAPClient(object):
search hit a server limit and its results are incomplete. search hit a server limit and its results are incomplete.
Keyword arguments: Keyword arguments:
attrs_list -- list of attributes to return, all if None (default None) :param attrs_list: list of attributes to return, all if None
base_dn -- dn of the entry at which to start the search (default '') (default None)
scope -- search scope, see LDAP docs (default ldap2.SCOPE_SUBTREE) :param base_dn: dn of the entry at which to start the search
time_limit -- time limit in seconds (default unlimited) (default '')
size_limit -- size (number of entries returned) limit :param scope: search scope, see LDAP docs (default ldap2.SCOPE_SUBTREE)
(default unlimited) :param time_limit: time limit in seconds (default unlimited)
paged_search -- search using paged results control :param size_limit: size (number of entries returned) limit
(default unlimited)
:param paged_search: search using paged results control
:param get_effective_rights: use GetEffectiveRights control
:raises: errors.NotFound if result set is empty :raises: errors.NotFound if result set is empty
or base_dn doesn't exist or base_dn doesn't exist
@@ -1391,7 +1397,10 @@ class LDAPClient(object):
if attrs_list: if attrs_list:
attrs_list = [a.lower() for a in set(attrs_list)] attrs_list = [a.lower() for a in set(attrs_list)]
sctrls = None base_sctrls = []
if get_effective_rights:
base_sctrls.append(self.__get_effective_rights_control())
cookie = '' cookie = ''
page_size = (size_limit if size_limit > 0 else 2000) - 1 page_size = (size_limit if size_limit > 0 else 2000) - 1
if page_size == 0: if page_size == 0:
@@ -1405,7 +1414,11 @@ class LDAPClient(object):
while True: while True:
if paged_search: if paged_search:
sctrls = [SimplePagedResultsControl(0, page_size, cookie)] sctrls = base_sctrls + [
SimplePagedResultsControl(0, page_size, cookie)
]
else:
sctrls = base_sctrls or None
try: try:
id = self.conn.search_ext( id = self.conn.search_ext(
@@ -1468,6 +1481,12 @@ class LDAPClient(object):
return (res, truncated) return (res, truncated)
def __get_effective_rights_control(self):
"""Construct a GetEffectiveRights control for current user."""
bind_dn = self.conn.whoami_s()[4:]
return GetEffectiveRightsControl(
True, "dn: {0}".format(bind_dn).encode('utf-8'))
def find_entry_by_attr(self, attr, value, object_class, attrs_list=None, def find_entry_by_attr(self, attr, value, object_class, attrs_list=None,
base_dn=None): base_dn=None):
""" """
@@ -1493,7 +1512,7 @@ class LDAPClient(object):
return entries[0] return entries[0]
def get_entry(self, dn, attrs_list=None, time_limit=None, def get_entry(self, dn, attrs_list=None, time_limit=None,
size_limit=None): size_limit=None, get_effective_rights=False):
""" """
Get entry (dn, entry_attrs) by dn. Get entry (dn, entry_attrs) by dn.
@@ -1505,7 +1524,7 @@ class LDAPClient(object):
entries = self.get_entries( entries = self.get_entries(
dn, self.SCOPE_BASE, None, attrs_list, time_limit=time_limit, dn, self.SCOPE_BASE, None, attrs_list, time_limit=time_limit,
size_limit=size_limit size_limit=size_limit, get_effective_rights=get_effective_rights,
) )
return entries[0] return entries[0]
+1 -17
View File
@@ -38,8 +38,6 @@ from ipapython.dn import DN
from ipapython.ipaldap import (LDAPClient, AUTOBIND_AUTO, AUTOBIND_ENABLED, from ipapython.ipaldap import (LDAPClient, AUTOBIND_AUTO, AUTOBIND_ENABLED,
AUTOBIND_DISABLED) AUTOBIND_DISABLED)
from ldap.controls.simple import GetEffectiveRightsControl
from ipalib import Registry, errors, _ from ipalib import Registry, errors, _
from ipalib.crud import CrudBackend from ipalib.crud import CrudBackend
from ipalib.request import context from ipalib.request import context
@@ -274,22 +272,8 @@ class ldap2(CrudBackend, LDAPClient):
Returns 2 attributes, the attributeLevelRights for the given list of Returns 2 attributes, the attributeLevelRights for the given list of
attributes and the entryLevelRights for the entry itself. attributes and the entryLevelRights for the entry itself.
""" """
assert isinstance(dn, DN) assert isinstance(dn, DN)
return self.get_entry(dn, attrs_list, get_effective_rights=True)
bind_dn = self.conn.whoami_s()[4:]
sctrl = [
GetEffectiveRightsControl(
True, "dn: {0}".format(bind_dn).encode('utf-8'))
]
self.conn.set_option(_ldap.OPT_SERVER_CONTROLS, sctrl)
try:
entry = self.get_entry(dn, attrs_list)
finally:
# remove the control so subsequent operations don't include GER
self.conn.set_option(_ldap.OPT_SERVER_CONTROLS, [])
return entry
def can_write(self, dn, attr): def can_write(self, dn, attr):
"""Returns True/False if the currently bound user has write permissions """Returns True/False if the currently bound user has write permissions