Hi,

On Thursday 16 September 2010 20:16:56 Simo Sorce wrote:
> On Thu, 16 Sep 2010 17:50:28 +0200
> 
> Ralf Haferkamp <rha...@suse.de> wrote:
> > Hi,
> > 
> > On Thursday 09 September 2010 15:14:10 Ralf Haferkamp wrote:
[..]
> > Find a newer version of my patch attached. Actually it's 3 patches
> > now. Please review.
> > 
> > Patch1: This just adds a new flag to save_groups() to indicate that
> > the group's member attribute is already populated with the members'
> > sysdb DN (instead on LDAP DNs). As I need to lookup the group
> > members in sysdb anyway, when processing the group, this saves some
> > redundant
> > 
> >    sysdb lookups when storing the group.
> 
> This looks like a good idea.
> 
> > Patch2: This is a somewhat improved version of my last patch.
> > 
> >    - better error handling
> >    - limit the number of LDAP requests that are issued before
[..]
> This patch makes the main function very complex, I suggest that you at
> least create separate functions for each new tevent request you want
> to create, that is sort of a rule for sssd. (And it makes code
> digestible more often than not).
Find a new version attached. Does that look better? If that is not what 
you were referring to lets discuss it in IRC on monday.

Note, I needed to implement sdap_process_group_send() slightly differnent 
than the other _send functions in sdap_async_accounts(). Instead of 
returning as tevent_req* it does return a error code. The tevent_req* is 
returned via the argument list.  I did this because there are cases where 
sdap_process_group_send() does not need to create a new tevent_req*, e.g. 
when the groups doesn't have any members or when all groupmembers are 
already cached.
 
> As for group unrolling I have also started working on it (ticket
> #625), although I am doing that in the 1.2.x branch as we need the
> functionality there too. I will try to post a patch soon so that we
> can compare relative approaches and merge the effort, ok ?
Fine. Feel free to take what you need from my patch.

> > Patch3: This adds a new config option to "ldap_unroll_group_members"
> > to enable/disable group unrolling
> 
> Can we use the followin patch instead ?
> http://fedorapeople.org/gitweb?p=simo/public_git/sssd.git;a=commitdiff
> ;h=fedf324be284de71e5dbf22f0135e9f681a15bde
> 
> This patch assumes the code will consider a nesting level of 0 as "no
> nesting". therefore it will embed in a single option both a way to
> enable disable unrolling and a limit on the level of nesting we will
> allow on the client (to avoid loops or very long delay on pathological
> cases).
Hm, just to make sure I understand your approach:
"ldap_group_nesting_level = 0" would be equal to 
"ldap_unroll_group_members = false" -> resulting in the current getgrnam 
behavior of only returning cached members.

"ldap_group_nesting_level = 1" would mean "ldap_unroll_group_members = 
true" -> return only direct members of the group and ignore mested 
groups.

while "ldap_group_nesting_level > 1" allow nesting up to a certain depth.

If that is correct what does "ldap_group_nesting_level = 0" mean for the 
initgroups() call? Return only groups that are already cached? I wonder 
if that would be a good idea.

-- 
Ralf
From 651a5c060c78aff8f7e5d3423d80a5471561c8ed Mon Sep 17 00:00:00 2001
From: Ralf Haferkamp <rha...@suse.de>
Date: Thu, 16 Sep 2010 17:24:17 +0200
Subject: [PATCH 1/3] Shortcut for save_group() to accept sysdb DNs as member attributes

Addtional parameter "sysdb_member_dns" for save_group() and save_groups()
to indicate that the "member" attribute of the groups is populated with
sysdb DNs of the members (instead of LDAP DNs).
---
 src/providers/ldap/sdap_async_accounts.c |   23 +++++++++++++++++++----
 1 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
index 8999ba0..d1c6378 100644
--- a/src/providers/ldap/sdap_async_accounts.c
+++ b/src/providers/ldap/sdap_async_accounts.c
@@ -609,6 +609,7 @@ static int sdap_save_group(TALLOC_CTX *memctx,
                            struct sss_domain_info *dom,
                            struct sysdb_attrs *attrs,
                            bool store_members,
+                           bool sysdb_member_dns,
                            char **_timestamp)
 {
     struct ldb_message_element *el;
@@ -697,7 +698,19 @@ static int sdap_save_group(TALLOC_CTX *memctx,
         }
     }
 
-    if (store_members) {
+    if (sysdb_member_dns) {
+        struct ldb_message_element *el1;
+        ret = sysdb_attrs_get_el(attrs, opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el1);
+        if (ret != EOK) {
+            goto fail;
+        }
+        ret = sysdb_attrs_get_el(group_attrs, SYSDB_MEMBER, &el);
+        if (ret != EOK) {
+            goto fail;
+        }
+        el->values = el1->values;
+        el->num_values = el1->num_values;
+    } else if (store_members) {
         ret = sysdb_attrs_get_el(attrs,
                         opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
         if (ret != EOK) {
@@ -808,6 +821,7 @@ static int sdap_save_groups(TALLOC_CTX *memctx,
                             struct sdap_options *opts,
                             struct sysdb_attrs **groups,
                             int num_groups,
+                            bool sysdb_member_dns,
                             char **_timestamp)
 {
     TALLOC_CTX *tmpctx;
@@ -848,7 +862,7 @@ static int sdap_save_groups(TALLOC_CTX *memctx,
         /* if 2 pass savemembers = false */
         ret = sdap_save_group(tmpctx, sysdb,
                               opts, dom, groups[i],
-                              (!twopass), &timestamp);
+                              (!twopass), sysdb_member_dns, &timestamp);
 
         /* Do not fail completely on errors.
          * Just report the failure to save and go on */
@@ -872,7 +886,7 @@ static int sdap_save_groups(TALLOC_CTX *memctx,
         }
     }
 
-    if (twopass) {
+    if (twopass && !sysdb_member_dns) {
 
         for (i = 0; i < num_groups; i++) {
 
@@ -988,6 +1002,7 @@ static void sdap_get_groups_process(struct tevent_req *subreq)
     ret = sdap_save_groups(state, state->sysdb,
                            state->dom, state->opts,
                            state->groups, state->count,
+                           false,
                            &state->higher_timestamp);
     if (ret) {
         DEBUG(2, ("Failed to store groups.\n"));
@@ -1354,7 +1369,7 @@ static void sdap_initgr_nested_store(struct tevent_req *req)
     state = tevent_req_data(req, struct sdap_initgr_nested_state);
 
     ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
-                           state->groups, state->groups_cur, NULL);
+                           state->groups, state->groups_cur, false, NULL);
     if (ret) {
         tevent_req_error(req, ret);
         return;
-- 
1.7.1

From 980d95b8e1d768c125ca6fcc9de6f18b0102138c Mon Sep 17 00:00:00 2001
From: Ralf Haferkamp <rha...@suse.de>
Date: Thu, 16 Sep 2010 17:24:17 +0200
Subject: [PATCH 2/3] Return all group members from getgr(nam|gid)

getgrnam()/getgrgid() should return all group members instead of only those
which have already been cached (in sysdb). To achieve this every member
that is currently not in the cache is looked up via LDAP and saved to the
cache.
---
 src/providers/ldap/sdap_async_accounts.c |  411 +++++++++++++++++++++++++++++-
 1 files changed, 402 insertions(+), 9 deletions(-)

diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
index d1c6378..7a6df8c 100644
--- a/src/providers/ldap/sdap_async_accounts.c
+++ b/src/providers/ldap/sdap_async_accounts.c
@@ -4,6 +4,7 @@
     Async LDAP Helper routines
 
     Copyright (C) Simo Sorce <sso...@redhat.com> - 2009
+    Copyright (C) 2010, Ralf Haferkamp <rha...@suse.de>, Novell Inc.
 
     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
@@ -931,6 +932,25 @@ struct sdap_get_groups_state {
     char *higher_timestamp;
     struct sysdb_attrs **groups;
     size_t count;
+    size_t check_count;
+};
+
+struct sdap_process_group_state {
+    struct tevent_context *ev;
+    struct sdap_options *opts;
+    struct sdap_handle *sh;
+    struct sss_domain_info *dom;
+    struct sysdb_ctx *sysdb;
+
+    struct sysdb_attrs *group;
+    struct sysdb_attrs **new_members;
+    struct ldb_message_element* sysdb_dns;
+    char **queued_members;
+    const char **attrs;
+    const char *filter;
+    size_t queue_idx;
+    size_t count;
+    size_t check_count;
 };
 
 static void sdap_get_groups_process(struct tevent_req *subreq);
@@ -976,13 +996,27 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
     return req;
 }
 
+
+static int sdap_process_group_send(TALLOC_CTX *memctx,
+                                  struct tevent_context *ev,
+                                  struct sss_domain_info *dom,
+                                  struct sysdb_ctx *sysdb,
+                                  struct sdap_options *opts,
+                                  struct sdap_handle *sh,
+                                  struct sysdb_attrs *group,
+                                  struct tevent_req **req);
+
+static void sdap_process_group_done(struct tevent_req *subreq);
+
+#define GROUPMEMBER_REQ_PARALLEL 50
 static void sdap_get_groups_process(struct tevent_req *subreq)
 {
     struct tevent_req *req = tevent_req_callback_data(subreq,
                                                       struct tevent_req);
     struct sdap_get_groups_state *state = tevent_req_data(req,
                                             struct sdap_get_groups_state);
-    int ret;
+    int ret,i;
+    int num_groups;
 
     ret = sdap_get_generic_recv(subreq, state,
                                 &state->count, &state->groups);
@@ -999,20 +1033,379 @@ static void sdap_get_groups_process(struct tevent_req *subreq)
         return;
     }
 
-    ret = sdap_save_groups(state, state->sysdb,
-                           state->dom, state->opts,
-                           state->groups, state->count,
-                           false,
-                           &state->higher_timestamp);
+    state->check_count = state->count;
+
+    for (i=0, num_groups=state->count; i < num_groups; i++) {
+        ret = sdap_process_group_send(state, state->ev, state->dom,
+                                    state->sysdb, state->opts,
+                                    state->sh, state->groups[i],
+                                    &subreq);
+
+        if (ret) {
+            tevent_req_error(req, ret);
+            return;
+        }
+        if (!subreq) {
+            /*
+             * no subrequest was need, all groupmember already cached or
+             * this is a group without members.
+             */
+            state->check_count--;
+        } else {
+            tevent_req_set_callback(subreq, sdap_process_group_done, req);
+        }
+    }
+
+    if (state->check_count==0) {
+        /*
+         * all members of the groups are either cached or
+         * all groups where empty
+         */
+        ret = sdap_save_groups(state, state->sysdb, state->dom,
+                            state->opts, state->groups, state->count,
+                            true, &state->higher_timestamp);
+        if (ret) {
+            DEBUG(2, ("Failed to store groups.\n"));
+            tevent_req_error(req, ret);
+            return;
+        }
+        DEBUG(9, ("Saving %d Groups - Done\n", state->count));
+
+        tevent_req_done(req);
+    }
+}
+
+static void sdap_groupmember_process(struct tevent_req *subreq);
+
+int sdap_process_group_send(TALLOC_CTX *memctx,
+                           struct tevent_context *ev,
+                           struct sss_domain_info *dom,
+                           struct sysdb_ctx *sysdb,
+                           struct sdap_options *opts,
+                           struct sdap_handle *sh,
+                           struct sysdb_attrs *group,
+                           struct tevent_req **req)
+{
+    struct ldb_message_element *el;
+    struct sdap_process_group_state *grp_state;
+    const char** attrs;
+    char* filter;
+    int queue_len;
+    int ret,i;
+
+    ret = sysdb_attrs_get_el(group,
+                            opts->group_map[SDAP_AT_GROUP_NAME].sys_name, &el);
+    if (ret) return ret;
+    if (el->num_values == 0) return ENOENT;
+
+    DEBUG(2, ("Processing Group %s\n", (const char*)el->values[0].data));
+    *req = tevent_req_create(memctx, &grp_state, struct sdap_process_group_state);
+    if (!*req) return ENOMEM;
+
+    ret = build_attrs_from_map(memctx, opts->user_map, SDAP_OPTS_USER, &attrs);
+    if (ret) {
+        goto fail;
+    }
+
+    /* FIXME: we ignore nested rfc2307bis groups for now */
+    filter = talloc_asprintf(memctx, "(objectclass=%s)",
+                             opts->user_map[SDAP_OC_USER].name);
+    if (!filter) {
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    grp_state->ev = ev;
+    grp_state->opts = opts;
+    grp_state->dom = dom;
+    grp_state->sh = sh;
+    grp_state->sysdb = sysdb;
+    grp_state->group =  group;
+    grp_state->check_count = 0;
+    grp_state->new_members = NULL;
+    grp_state->queue_idx = 0;
+    grp_state->queued_members = NULL;
+    grp_state->filter = filter;
+    grp_state->attrs = attrs;
+
+    ret = sysdb_attrs_get_el(group,opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
+    if (ret) {
+        goto fail;
+    }
+
+    /* Group without members */
+    if ( el->num_values == 0 ) {
+        DEBUG(2, ("No Members. Done!\n"));
+        talloc_zfree(*req);
+        *req = NULL;
+        return EOK;
+    }
+
+    grp_state->sysdb_dns = talloc(memctx, struct ldb_message_element);
+    if (!grp_state->sysdb_dns) {
+        ret = ENOMEM;
+        goto fail;
+    }
+    grp_state->sysdb_dns->values = talloc_array(memctx, struct ldb_val , el->num_values);
+    if (!grp_state->sysdb_dns->values) {
+        ret = ENOMEM;
+        goto fail;
+    }
+    grp_state->sysdb_dns->num_values = 0;
+    queue_len=0;
+
+    /*
+     * For each member check if it is already present in sysdb,
+     * if it isn't read it from LDAP
+     */
+    for (i=0; i < el->num_values; i++) {
+        char *sysdb_dn;
+        DEBUG(7, ("checking member: %s\n", (const char*)el->values[i].data));
+        ret = sdap_find_entry_by_origDN(memctx, sysdb, dom,
+                                        (const char *)el->values[i].data,
+                                        &sysdb_dn);
+        if (ret == ENOENT) {
+            if ((opts->schema_type != SDAP_SCHEMA_RFC2307BIS) ||
+                (! dp_opt_get_bool(opts->basic, SDAP_UNROLL_GROUP_MEMBERS)))
+                continue;
+
+            DEBUG(7, ("member #%d (%s): not found in sysdb, searching LDAP\n",
+                    i, (char *)el->values[i].data));
+
+            /*
+             * Issue at most GROUPMEMBER_REQ_PARALLEL LDAP searches at once.
+             * The rest is sent while the results are being processed.
+             * We limit the number as of request here, as the Server might
+             * enforce limits on the number of pending operations per connection.
+             */
+            if (grp_state->check_count > GROUPMEMBER_REQ_PARALLEL ) {
+                DEBUG(7, (" queueing search for: %s\n",
+                         (char*)el->values[i].data));
+                if (! grp_state->queued_members ) {
+                    DEBUG(7,("Allocating queue for %d members\n",
+                            el->num_values - grp_state->check_count));
+                    grp_state->queued_members = talloc_array(grp_state, char*,
+                            el->num_values - grp_state->check_count+1);
+                    if (!grp_state->queued_members) {
+                        ret = ENOMEM;
+                        goto fail;
+                    }
+                }
+                grp_state->queued_members[queue_len]=(char*)el->values[i].data;
+                queue_len++;
+            } else {
+                struct tevent_req *subreq =
+                        sdap_get_generic_send(grp_state,
+                                            ev, opts, sh,
+                                            (char *)el->values[i].data,
+                                            LDAP_SCOPE_BASE,
+                                            filter,
+                                            attrs,
+                                            opts->user_map,
+                                            SDAP_OPTS_USER);
+                if (!subreq) {
+                    ret = ENOMEM;
+                    goto fail;
+                }
+                tevent_req_set_callback(subreq, sdap_groupmember_process, *req);
+            }
+            grp_state->check_count++;
+        } else {
+            /*
+             * User already cached in sysdb. Remember the sysdb DN for later use
+             * by sdap_save_groups()
+             */
+            DEBUG(7,("sysdbdn: %s\n", sysdb_dn));
+            grp_state->sysdb_dns->values[grp_state->sysdb_dns->num_values].data =
+                    (uint8_t*)sysdb_dn;
+            grp_state->sysdb_dns->values[grp_state->sysdb_dns->num_values].length =
+                    strlen(sysdb_dn);
+            grp_state->sysdb_dns->num_values++;
+        }
+    }
+    if (queue_len > 0)
+        grp_state->queued_members[queue_len]=NULL;
+
+    if (grp_state->check_count == 0) {
+        /*
+         * All group members are already cached in sysdb, we are done
+         * with this group. To avoid redundant sysdb lookups, populate the
+         * "member" attribute of the group entry with the sysdb DNs of
+         * the members.
+         */
+        el->values = grp_state->sysdb_dns->values;
+        el->num_values = grp_state->sysdb_dns->num_values;
+        talloc_zfree(*req);
+
+        *req = NULL;
+        return EOK;
+    } else {
+        grp_state->count = grp_state->check_count;
+        grp_state->new_members = talloc_zero_array(grp_state,
+                                                   struct sysdb_attrs *,
+                                                   grp_state->count + 1);
+        if (!grp_state->new_members) {
+            ret = ENOMEM;
+            goto fail;
+        }
+    }
+    return EOK;
+
+fail:
+    talloc_zfree(*req);
+    *req = NULL;
+    return ret;
+}
+
+static int sdap_process_group_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+static void sdap_process_group_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct sdap_get_groups_state *state = tevent_req_data(req,
+            struct sdap_get_groups_state);
+
+    int ret = sdap_process_group_recv(subreq);
+    talloc_zfree(subreq);
+
     if (ret) {
-        DEBUG(2, ("Failed to store groups.\n"));
         tevent_req_error(req, ret);
         return;
     }
 
-    DEBUG(9, ("Saving %d Groups - Done\n", state->count));
+    state->check_count--;
+    DEBUG(9, ("Groups remaining: %d\n", state->check_count));
 
-    tevent_req_done(req);
+
+    if (state->check_count == 0) {
+        DEBUG(9, ("All groups processed\n"));
+
+        ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
+                               state->groups, state->count, true,
+                               &state->higher_timestamp);
+        if (ret) {
+            DEBUG(2, ("Failed to store groups.\n"));
+            tevent_req_error(req, ret);
+            return;
+        }
+        DEBUG(9, ("Saving %d Groups - Done\n", state->count));
+        tevent_req_done(req);
+    }
+}
+
+static void sdap_groupmember_process(struct tevent_req *subreq)
+{
+    struct sysdb_attrs **usr_attrs;
+    size_t count;
+    int ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct sdap_process_group_state *state = tevent_req_data(req,
+            struct sdap_process_group_state);
+    struct ldb_message_element *el;
+    struct ldb_dn *dn;
+    char* dn_string;
+
+    state->check_count--;
+    DEBUG(9, ("Members remaining: %d\n", state->check_count));
+
+    ret = sdap_get_generic_recv(subreq, state, &count, &usr_attrs);
+    talloc_zfree(subreq);
+    if (ret) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (count != 1) {
+        DEBUG(2, ("Expected one user entry and got %d\n", count));
+        tevent_req_error(req, ENOENT);
+        return;
+    }
+    ret = sysdb_attrs_get_el(usr_attrs[0],
+            state->opts->user_map[SDAP_AT_USER_NAME].sys_name, &el);
+    if (el->num_values == 0) {
+        ret = EINVAL;
+    }
+    if (ret) {
+        DEBUG(1, ("Failed to get the member's name\n"));
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    /*
+     * Convert the just received DN into the corresponding sysdb DN
+     * for later usage by sdap_save_groups()
+     */
+    dn = sysdb_user_dn(state->sysdb, state, state->dom->name,
+                       (char*)el[0].values[0].data);
+    if (!dn) {
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+
+    dn_string = ldb_dn_alloc_linearized(state->group, dn);
+    if (!dn_string) {
+        tevent_req_error(req, ENOMEM);
+        return;
+    }
+
+    state->sysdb_dns->values[state->sysdb_dns->num_values].data =
+            (uint8_t*)dn_string;
+    state->sysdb_dns->values[state->sysdb_dns->num_values].length =
+            strlen(dn_string);
+    state->sysdb_dns->num_values++;
+
+    state->new_members[ state->check_count ] = usr_attrs[0];
+
+    /* Are there more searches for uncached users to submit ? */
+    if ( state->queued_members && state->queued_members[state->queue_idx] ) {
+        struct tevent_req *process_member_req;
+        process_member_req = sdap_get_generic_send(state,
+                                    state->ev,
+                                    state->opts,
+                                    state->sh,
+                                    state->queued_members[state->queue_idx],
+                                    LDAP_SCOPE_BASE,
+                                    state->filter,
+                                    state->attrs,
+                                    state->opts->user_map,
+                                    SDAP_OPTS_USER);
+        if (!process_member_req) {
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
+
+        tevent_req_set_callback(process_member_req, sdap_groupmember_process,
+                                req);
+        state->queue_idx++;
+    }
+
+    if (state->check_count == 0) {
+        ret = sdap_save_users(state, state->sysdb, state->dom, state->opts,
+                              state->new_members, state->count, NULL);
+        if (ret) {
+            DEBUG(2, ("Failed to store users.\n"));
+            tevent_req_error(req, ret);
+            return;
+        }
+
+        /*
+         * To avoid redundant sysdb lookups, populate the "member" attribute
+         * of the group entry with the sysdb DNs of the members.
+         */
+        ret = sysdb_attrs_get_el(state->group,
+            state->opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
+        el->values = state->sysdb_dns->values;
+        el->num_values = state->sysdb_dns->num_values;
+        DEBUG(9, ("Processed Group - Done\n"));
+        tevent_req_done(req);
+    }
 }
 
 int sdap_get_groups_recv(struct tevent_req *req,
-- 
1.7.1

From c16d2e27d1390b9cdc3bf90a9df2513031306b07 Mon Sep 17 00:00:00 2001
From: Ralf Haferkamp <rha...@suse.de>
Date: Thu, 16 Sep 2010 17:24:17 +0200
Subject: [PATCH 3/3] New config option to disable group unrolling

Adds the new sssd.conf switch "ldap_unroll_group_members" (defaults to
"true") for enabling/disabling the unrolling of group members during
getgrgid/getgrnam calls.
---
 src/providers/ldap/ldap_common.c |    1 +
 src/providers/ldap/sdap.h        |    1 +
 2 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index 14bdd28..23a0ccc 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -48,6 +48,7 @@ struct dp_option default_basic_opts[] = {
     { "ldap_group_search_scope", DP_OPT_STRING, { "sub" }, NULL_STRING },
     { "ldap_group_search_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING },
     { "ldap_schema", DP_OPT_STRING, { "rfc2307" }, NULL_STRING },
+    { "ldap_unroll_group_members", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE },
     { "ldap_offline_timeout", DP_OPT_NUMBER, { .number = 60 }, NULL_NUMBER },
     { "ldap_force_upper_case_realm", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE },
     { "ldap_enumeration_refresh_timeout", DP_OPT_NUMBER, { .number = 300 }, NULL_NUMBER },
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 4426dac..7093b7f 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -155,6 +155,7 @@ enum sdap_basic_opt {
     SDAP_GROUP_SEARCH_SCOPE,
     SDAP_GROUP_SEARCH_FILTER,
     SDAP_SCHEMA,
+    SDAP_UNROLL_GROUP_MEMBERS,
     SDAP_OFFLINE_TIMEOUT,
     SDAP_FORCE_UPPER_CASE_REALM,
     SDAP_ENUM_REFRESH_TIMEOUT,
-- 
1.7.1

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to