From 063dab6715f97aeeb6c29f5c3210f609cfea0f81 Mon Sep 17 00:00:00 2001
From: Ralf Haferkamp <rhafer@suse.de>
Date: Wed, 8 Sep 2010 10:30:03 +0200
Subject: [PATCH] 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 |  177 ++++++++++++++++++++++++++++--
 1 files changed, 169 insertions(+), 8 deletions(-)

diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
index 8999ba0..795c7ca 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 <ssorce@redhat.com> - 2009
+    Copyright (C) 2010, Ralf Haferkamp <rhafer@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
@@ -917,6 +918,20 @@ 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;
+    size_t count;
+    size_t check_count;
 };
 
 static void sdap_get_groups_process(struct tevent_req *subreq);
@@ -962,13 +977,22 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
     return req;
 }
 
+static void sdap_groupmember_process(struct tevent_req *subreq);
+static void sdap_group_process(struct tevent_req *subreq);
+
 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;
+    struct tevent_req *process_member_req;
+    struct tevent_req *process_grp_req;
+    struct ldb_message_element *el;
+    struct sdap_process_group_state *grp_state;
+    const char **attrs;
+    char *filter;
+    int ret,i,k;
 
     ret = sdap_get_generic_recv(subreq, state,
                                 &state->count, &state->groups);
@@ -985,19 +1009,156 @@ 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,
-                           &state->higher_timestamp);
+    state->check_count = state->count;
+
+    ret = build_attrs_from_map(state, state->opts->user_map, SDAP_OPTS_USER,
+                               &attrs);
+    filter = talloc_asprintf(state, "(objectclass=%s)",
+                             state->opts->user_map[SDAP_OC_USER].name);
+
+    for (i=0; i < state->count; i++) {
+        ret = sysdb_attrs_get_el(state->groups[i],
+                state->opts->group_map[SDAP_AT_GROUP_NAME].sys_name, &el);
+        if (ret || el->num_values == 0) {
+            tevent_req_error(req, ENOENT);
+            return;
+        }
+
+        DEBUG(2, ("Processing Group %s\n", (const char*)el->values[0].data));
+        process_grp_req = tevent_req_create(state, &grp_state,
+                                            struct sdap_process_group_state);
+        if (!process_grp_req) {
+            tevent_req_error(req, ENOMEM);
+            return;
+        }
+
+        grp_state->ev = state->ev;
+        grp_state->opts = state->opts;
+        grp_state->dom = state->dom;
+        grp_state->sh = state->sh;
+        grp_state->sysdb = state->sysdb;
+        grp_state->group =  state->groups[i];
+        grp_state->check_count = 0;
+        grp_state->new_members = NULL;
+        tevent_req_set_callback(process_grp_req, sdap_group_process, req);
+
+        ret = sysdb_attrs_get_el(state->groups[i],
+                state->opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
+        if (ret) {
+            /* FIXME: Is this really the correct thing todo? */
+            tevent_req_error(req, ENOENT);
+            return;
+        }
+
+        /*
+         * For each member check if it is already present in sysdb,
+         * if it isn't read it from LDAP
+         */
+        for (k=0; k < el->num_values; k++) {
+            char *tmp;
+            DEBUG(7, ("checking member: %s\n",
+                      (const char*)el->values[k].data));
+            ret = sdap_find_entry_by_origDN(grp_state, grp_state->sysdb,
+                                            grp_state->dom,
+                                            (const char *)el->values[k].data,
+                                            &tmp);
+            if (ret == ENOENT) {
+                DEBUG(7, ("    member #%d (%s): not found in sysdb, searching LDAP\n",
+                        k, (char *)el->values[k].data));
+                grp_state->check_count++;
+                process_member_req = sdap_get_generic_send(grp_state,
+                                            grp_state->ev,
+                                            grp_state->opts,
+                                            grp_state->sh,
+                                            (char *)el->values[k].data,
+                                            LDAP_SCOPE_BASE,
+                                            filter,
+                                            attrs,
+                                            grp_state->opts->user_map,
+                                            SDAP_OPTS_USER);
+                tevent_req_set_callback(process_member_req, sdap_groupmember_process,
+                                        process_grp_req);
+            }
+        }
+
+        if (grp_state->check_count == 0) {
+            /*
+             * All group members are already cached in sysdb, we are done
+             * with this group
+             */
+            tevent_req_done(process_grp_req);
+        } else {
+            grp_state->count = grp_state->check_count;
+            grp_state->new_members = talloc_zero_array(grp_state,
+                                                       struct sysdb_attrs *,
+                                                       grp_state->count + 1);
+        }
+    }
+}
+
+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);
+
+    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) {
-        DEBUG(2, ("Failed to store groups.\n"));
         tevent_req_error(req, ret);
         return;
     }
 
-    DEBUG(9, ("Saving %d Groups - Done\n", state->count));
+    if (count != 1) {
+        DEBUG(2, ("Expected one user entry and got %d\n", count));
+        tevent_req_error(req, ENOENT);
+        return;
+    }
 
-    tevent_req_done(req);
+    state->new_members[ state->check_count ] = usr_attrs[0];
+
+    if (state->check_count == 0) {
+        ret = sdap_save_users(state, state->sysdb, state->dom, state->opts,
+                              state->new_members, state->count, NULL);
+        DEBUG(9, ("Processed Group - Done\n"));
+        tevent_req_done(req);
+    }
+}
+
+static void sdap_group_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);
+
+    state->check_count--;
+    DEBUG(9, ("Groups remaining: %d\n", state->check_count));
+
+    talloc_zfree(subreq);
+
+    if (state->check_count == 0) {
+        int ret;
+        DEBUG(9, ("All groups processed\n"));
+
+        ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
+                               state->groups, state->count,
+                               &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);
+    }
 }
 
 int sdap_get_groups_recv(struct tevent_req *req,
-- 
1.7.1

