>From f5c78332676e705409fb8f9d4c013348b46740f2 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek@redhat.com>
Date: Fri, 2 May 2014 19:17:38 +0200
Subject: [PATCH 2/2] IFP: Add a GetGroupsList method

This patch adds a new method on the bus with the following synopsis:
<method name="GetUserGroups">
    <arg name="user" type="s" direction="in" />
    <arg name="values" type="as" direction="out"/>
</method>

Its purpose is to return names of groups the user is a member of as a
list of strings.
---
 Makefile.am                             |   1 +
 src/responder/ifp/ifp_iface.xml         |   5 +
 src/responder/ifp/ifp_iface_generated.c |  45 ++++++++
 src/responder/ifp/ifp_iface_generated.h |   5 +
 src/responder/ifp/ifp_private.h         |   3 +
 src/responder/ifp/ifpsrv.c              |   1 +
 src/responder/ifp/ifpsrv_cmd.c          | 181 +++++++++++++++++++++++++++++---
 7 files changed, 227 insertions(+), 14 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 0f12bdbfe74fdc589e0d4e16b417d59aa8466d90..35ab9d7860b8de8c4562903782017c91fe82fe2f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1710,6 +1710,7 @@ ifp_tests_SOURCES = \
      $(TEST_MOCK_RESP_OBJ) \
     src/tests/cmocka/test_ifp.c \
     src/responder/ifp/ifpsrv_cmd.c \
+    src/responder/ifp/ifp_iface_generated.c \
     src/responder/ifp/ifpsrv_util.c
 ifp_tests_CFLAGS = \
     $(AM_CFLAGS)
diff --git a/src/responder/ifp/ifp_iface.xml b/src/responder/ifp/ifp_iface.xml
index de6acce2f55afcda8f4028e3ba18825a28537e52..4893e2df5bd23ff8c3a445e7a665b374586eb90f 100644
--- a/src/responder/ifp/ifp_iface.xml
+++ b/src/responder/ifp/ifp_iface.xml
@@ -16,5 +16,10 @@
             <annotation name="org.freedesktop.sssd.RawHandler" value="true"/>
         </method>
 
+        <method name="GetUserGroups">
+            <arg name="user" type="s" direction="in" />
+            <arg name="values" type="as" direction="out"/>
+        </method>
+
     </interface>
 </node>
diff --git a/src/responder/ifp/ifp_iface_generated.c b/src/responder/ifp/ifp_iface_generated.c
index ed8d651872af892f0d5996e335410096696a8ec4..d3cb4990cf13fd51621b7606371cdf3175bc789a 100644
--- a/src/responder/ifp/ifp_iface_generated.c
+++ b/src/responder/ifp/ifp_iface_generated.c
@@ -5,6 +5,9 @@
 #include "sbus/sssd_dbus_meta.h"
 #include "ifp_iface_generated.h"
 
+/* invokes a handler with a 's' DBus signature */
+static int invoke_s_method(struct sbus_request *dbus_req, void *function_ptr);
+
 /* arguments for org.freedesktop.sssd.infopipe.GetUserAttr */
 const struct sbus_arg_meta infopipe_iface_GetUserAttr__in[] = {
     { "user", "s" },
@@ -18,6 +21,25 @@ const struct sbus_arg_meta infopipe_iface_GetUserAttr__out[] = {
     { NULL, }
 };
 
+/* arguments for org.freedesktop.sssd.infopipe.GetUserGroups */
+const struct sbus_arg_meta infopipe_iface_GetUserGroups__in[] = {
+    { "user", "s" },
+    { NULL, }
+};
+
+/* arguments for org.freedesktop.sssd.infopipe.GetUserGroups */
+const struct sbus_arg_meta infopipe_iface_GetUserGroups__out[] = {
+    { "values", "as" },
+    { NULL, }
+};
+
+int infopipe_iface_GetUserGroups_finish(struct sbus_request *req, const char *arg_values[], int len_values)
+{
+   return sbus_request_return_and_finish(req,
+                                         DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &arg_values, len_values,
+                                         DBUS_TYPE_INVALID);
+}
+
 /* methods for org.freedesktop.sssd.infopipe */
 const struct sbus_method_meta infopipe_iface__methods[] = {
     {
@@ -34,6 +56,13 @@ const struct sbus_method_meta infopipe_iface__methods[] = {
         offsetof(struct infopipe_iface, GetUserAttr),
         NULL, /* no invoker */
     },
+    {
+        "GetUserGroups", /* name */
+        infopipe_iface_GetUserGroups__in,
+        infopipe_iface_GetUserGroups__out,
+        offsetof(struct infopipe_iface, GetUserGroups),
+        invoke_s_method,
+    },
     { NULL, }
 };
 
@@ -44,3 +73,19 @@ const struct sbus_interface_meta infopipe_iface_meta = {
     NULL, /* no signals */
     NULL, /* no propetries */
 };
+
+/* invokes a handler with a 's' DBus signature */
+static int invoke_s_method(struct sbus_request *dbus_req, void *function_ptr)
+{
+    const char * arg_0;
+    int (*handler)(struct sbus_request *, void *, const char *) = function_ptr;
+
+    if (!sbus_request_parse_or_finish(dbus_req,
+                               DBUS_TYPE_STRING, &arg_0,
+                               DBUS_TYPE_INVALID)) {
+         return EOK; /* request handled */
+    }
+
+    return (handler)(dbus_req, dbus_req->intf->instance_data,
+                     arg_0);
+}
diff --git a/src/responder/ifp/ifp_iface_generated.h b/src/responder/ifp/ifp_iface_generated.h
index c52e87f06fea797bd73f451b9057b406397219a9..9b92b9b071a85bc073f13114923c4cf4903d9bdf 100644
--- a/src/responder/ifp/ifp_iface_generated.h
+++ b/src/responder/ifp/ifp_iface_generated.h
@@ -15,6 +15,7 @@
 #define INFOPIPE_IFACE "org.freedesktop.sssd.infopipe"
 #define INFOPIPE_IFACE_PING "Ping"
 #define INFOPIPE_IFACE_GETUSERATTR "GetUserAttr"
+#define INFOPIPE_IFACE_GETUSERGROUPS "GetUserGroups"
 
 /* ------------------------------------------------------------------------
  * DBus handlers
@@ -39,8 +40,12 @@ struct infopipe_iface {
     struct sbus_vtable vtable; /* derive from sbus_vtable */
     sbus_msg_handler_fn Ping;
     sbus_msg_handler_fn GetUserAttr;
+    int (*GetUserGroups)(struct sbus_request *req, void *data, const char *arg_user);
 };
 
+/* finish function for GetUserGroups */
+int infopipe_iface_GetUserGroups_finish(struct sbus_request *req, const char *arg_values[], int len_values);
+
 /* ------------------------------------------------------------------------
  * DBus Interface Metadata
  *
diff --git a/src/responder/ifp/ifp_private.h b/src/responder/ifp/ifp_private.h
index c03cf6ccc8767de4ffeb469bfea84f06a4da3441..41598c819bfe924dc1d1c81c6b8c8c609705adcd 100644
--- a/src/responder/ifp/ifp_private.h
+++ b/src/responder/ifp/ifp_private.h
@@ -52,6 +52,9 @@ int ifp_ping(struct sbus_request *dbus_req, void *data);
 
 int ifp_user_get_attr(struct sbus_request *dbus_req, void *data);
 
+int ifp_user_get_groups(struct sbus_request *req,
+                         void *data, const char *arg_user);
+
 /* == Utility functions == */
 struct ifp_req {
     struct sbus_request *dbus_req;
diff --git a/src/responder/ifp/ifpsrv.c b/src/responder/ifp/ifpsrv.c
index e02b5f08a1c9ef5ecec518020c015df7005d4612..b221ced7047115fcf4439f85cc02793a3d01f274 100644
--- a/src/responder/ifp/ifpsrv.c
+++ b/src/responder/ifp/ifpsrv.c
@@ -67,6 +67,7 @@ struct infopipe_iface ifp_iface = {
     { &infopipe_iface_meta, 0 },
     .Ping = ifp_ping,
     .GetUserAttr = ifp_user_get_attr,
+    .GetUserGroups = ifp_user_get_groups,
 };
 
 struct sss_cmd_table *get_ifp_cmds(void)
diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
index cd9ab44412164ee8d7e763177810feb87287779b..0cc9d144c0790f4ac06822cb8b2eafe26b4de5c0 100644
--- a/src/responder/ifp/ifpsrv_cmd.c
+++ b/src/responder/ifp/ifpsrv_cmd.c
@@ -35,6 +35,7 @@ struct ifp_attr_req {
 static struct tevent_req *
 ifp_user_get_attr_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx,
                        struct sss_nc_ctx *ncache, int neg_timeout,
+                       enum sss_dp_acct_type search_type,
                        const char *inp, const char **attrs);
 static errno_t ifp_user_get_attr_recv(TALLOC_CTX *mem_ctx,
                                       struct tevent_req *req,
@@ -84,6 +85,7 @@ int ifp_user_get_attr(struct sbus_request *dbus_req, void *data)
 
     req = ifp_user_get_attr_send(ireq, ifp_ctx->rctx,
                                  ifp_ctx->ncache, ifp_ctx->neg_timeout,
+                                 SSS_DP_USER,
                                  attr_req->name, attr_req->attrs);
     if (req == NULL) {
         return sbus_request_finish(dbus_req, NULL);
@@ -163,15 +165,17 @@ static void ifp_user_get_attr_process(struct tevent_req *req)
     ret = ifp_user_get_attr_handle_reply(attr_req->ireq, attr_req->name,
                                          attr_req->attrs, res);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE, "Could not handle reply!\n");
-        /* Nothing to do, let the client time out */
+        sbus_request_fail_and_finish(attr_req->ireq->dbus_req,
+                               sbus_error_new(attr_req->ireq->dbus_req,
+                                              DBUS_ERROR_FAILED,
+                                              "Failed to build a reply\n"));
         return;
     }
 }
 
 static errno_t
 ifp_user_get_attr_handle_reply(struct ifp_req *ireq, const char *user,
-                                const char **attrs, struct ldb_result *res)
+                               const char **attrs, struct ldb_result *res)
 {
     errno_t ret;
     dbus_bool_t dbret;
@@ -228,11 +232,135 @@ ifp_user_get_attr_handle_reply(struct ifp_req *ireq, const char *user,
     return sbus_request_finish(ireq->dbus_req, reply);
 }
 
+static void ifp_user_get_groups_process(struct tevent_req *req);
+static errno_t ifp_user_get_groups_reply(struct ifp_req *ireq,
+                                         const char *user,
+                                         struct ldb_result *res);
+
+int ifp_user_get_groups(struct sbus_request *dbus_req,
+                         void *data, const char *arg_user)
+{
+    struct ifp_req *ireq;
+    struct ifp_ctx *ifp_ctx;
+    struct ifp_attr_req *group_req;
+    struct tevent_req *req;
+    errno_t ret;
+
+    ifp_ctx = talloc_get_type(data, struct ifp_ctx);
+    if (ifp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
+        return sbus_request_return_and_finish(dbus_req, DBUS_TYPE_INVALID);
+    }
+
+    ret = ifp_req_create(dbus_req, ifp_ctx, &ireq);
+    if (ret != EOK) {
+        return ifp_req_create_handle_failure(dbus_req, ret);
+    }
+
+    group_req = talloc_zero(ireq, struct ifp_attr_req);
+    if (group_req == NULL) {
+        return sbus_request_finish(dbus_req, NULL);
+    }
+    group_req->ireq = ireq;
+    group_req->name = arg_user;
+
+    group_req->attrs = talloc_zero_array(group_req, const char *, 2);
+    if (group_req->attrs == NULL) {
+        return sbus_request_finish(dbus_req, NULL);
+    }
+
+    group_req->attrs[0] = talloc_strdup(group_req->attrs, SYSDB_MEMBEROF);
+    if (group_req->attrs[0] == NULL) {
+        return sbus_request_finish(dbus_req, NULL);
+    }
+
+    DEBUG(SSSDBG_FUNC_DATA,
+          "Looking up groups of user [%s] on behalf of %"PRIi64"\n",
+          group_req->name, group_req->ireq->dbus_req->client);
+
+    req = ifp_user_get_attr_send(ireq, ifp_ctx->rctx,
+                                 ifp_ctx->ncache, ifp_ctx->neg_timeout,
+                                 SSS_DP_INITGROUPS,
+                                 group_req->name, group_req->attrs);
+    if (req == NULL) {
+        return sbus_request_finish(dbus_req, NULL);
+    }
+    tevent_req_set_callback(req, ifp_user_get_groups_process, group_req);
+    return EOK;
+}
+
+static void ifp_user_get_groups_process(struct tevent_req *req)
+{
+    struct ifp_attr_req *group_req;
+    errno_t ret;
+    struct ldb_result *res;
+
+    group_req = tevent_req_callback_data(req, struct ifp_attr_req);
+
+    ret = ifp_user_get_attr_recv(group_req, req, &res);
+    talloc_zfree(req);
+    if (ret == ENOENT) {
+        sbus_request_fail_and_finish(group_req->ireq->dbus_req,
+                               sbus_error_new(group_req->ireq->dbus_req,
+                                              DBUS_ERROR_FAILED,
+                                              "No such user\n"));
+        return;
+    } else if (ret != EOK) {
+        sbus_request_fail_and_finish(group_req->ireq->dbus_req,
+                               sbus_error_new(group_req->ireq->dbus_req,
+                                              DBUS_ERROR_FAILED,
+                                              "Failed to read attribute\n"));
+        return;
+    }
+
+    ret = ifp_user_get_groups_reply(group_req->ireq, group_req->name,
+                                     res);
+    if (ret != EOK) {
+        sbus_request_fail_and_finish(group_req->ireq->dbus_req,
+                               sbus_error_new(group_req->ireq->dbus_req,
+                                              DBUS_ERROR_FAILED,
+                                              "Failed to build a reply\n"));
+        return;
+    }
+}
+
+static errno_t
+ifp_user_get_groups_reply(struct ifp_req *ireq, const char *user,
+                          struct ldb_result *res)
+{
+    int i, num;
+    const char *name;
+    const char **groupnames;
+
+    /* one less, the first one is the user entry */
+    num = res->count - 1;
+    groupnames = talloc_zero_array(ireq, const char *, num);
+    if (groupnames == NULL) {
+        return sbus_request_finish(ireq->dbus_req, NULL);
+    }
+
+    for (i = 0; i < num; i++) {
+        name = ldb_msg_find_attr_as_string(res->msgs[i + 1], SYSDB_NAME, NULL);
+        if (name == NULL) {
+            DEBUG(SSSDBG_MINOR_FAILURE, "Skipping a group with no name\n");
+            continue;
+        }
+
+        DEBUG(SSSDBG_TRACE_FUNC, "Adding group %s\n", name);
+        groupnames[i] = name;
+    }
+
+    return infopipe_iface_GetUserGroups_finish(ireq->dbus_req,
+                                               groupnames, num);
+}
+
 struct ifp_user_get_attr_state {
     const char *inp;
     const char **attrs;
     struct ldb_result *res;
 
+    enum sss_dp_acct_type search_type;
+
     char *name;
     char *domname;
 
@@ -248,6 +376,7 @@ struct ifp_user_get_attr_state {
 static void ifp_user_get_attr_dom(struct tevent_req *subreq);
 static errno_t ifp_user_get_attr_search(struct tevent_req *req);
 int ifp_cache_check(struct ifp_user_get_attr_state *state,
+                    enum sss_dp_acct_type search_type,
                     sss_dp_callback_t callback,
                     unsigned int cache_refresh_percent,
                     void *pvt);
@@ -256,6 +385,7 @@ void ifp_user_get_attr_done(struct tevent_req *req);
 static struct tevent_req *
 ifp_user_get_attr_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx,
                        struct sss_nc_ctx *ncache, int neg_timeout,
+                       enum sss_dp_acct_type search_type,
                        const char *inp, const char **attrs)
 {
     errno_t ret;
@@ -272,6 +402,7 @@ ifp_user_get_attr_send(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx,
     state->rctx = rctx;
     state->ncache = ncache;
     state->neg_timeout = neg_timeout;
+    state->search_type = search_type;
 
     subreq = sss_parse_inp_send(req, rctx, inp);
     if (subreq == NULL) {
@@ -392,17 +523,32 @@ static errno_t ifp_user_get_attr_search(struct tevent_req *req)
         DEBUG(SSSDBG_FUNC_DATA,
               "Requesting info for [%s@%s]\n", name, dom->name);
 
-        ret = sysdb_get_user_attr(state, dom, name, state->attrs, &state->res);
+        switch (state->search_type) {
+            case SSS_DP_USER:
+                ret = sysdb_get_user_attr(state, dom, name,
+                                          state->attrs, &state->res);
+                break;
+            case SSS_DP_INITGROUPS:
+                ret = sysdb_initgroups(state, dom, name,
+                                       &state->res);
+                break;
+            default:
+                DEBUG(SSSDBG_OP_FAILURE, "Unsupported operation\n");
+                return EIO;
+        }
+
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Failed to make request to our cache!\n");
             return EIO;
         }
 
-        if (state->res->count > 1) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "getpwnam call returned more than one result !?!\n");
-            return ENOENT;
+        if (state->search_type == SSS_DP_USER) {
+            if (state->res->count > 1) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                        "getpwnam call returned more than one result !?!\n");
+                return ENOENT;
+            }
         }
 
         if (state->res->count == 0 && state->check_provider == false) {
@@ -427,7 +573,8 @@ static errno_t ifp_user_get_attr_search(struct tevent_req *req)
         /* if this is a caching provider (or if we haven't checked the cache
          * yet) then verify that the cache is uptodate */
         if (state->check_provider) {
-            ret = ifp_cache_check(state, ifp_dp_callback, 0, req);
+            ret = ifp_cache_check(state, state->search_type,
+                                  ifp_dp_callback, 0, req);
             if (ret != EOK) {
                 /* Anything but EOK means we should reenter the mainloop
                  * because we may be refreshing the cache
@@ -448,6 +595,7 @@ static errno_t ifp_user_get_attr_search(struct tevent_req *req)
 }
 
 int ifp_cache_check(struct ifp_user_get_attr_state *state,
+                    enum sss_dp_acct_type search_type,
                     sss_dp_callback_t callback,
                     unsigned int cache_refresh_percent,
                     void *pvt)
@@ -457,7 +605,7 @@ int ifp_cache_check(struct ifp_user_get_attr_state *state,
     struct tevent_req *req;
     struct dp_callback_ctx *cb_ctx = NULL;
 
-    if (state->res->count > 1) {
+    if (search_type == SSS_DP_USER && state->res->count > 1) {
         DEBUG(SSSDBG_OP_FAILURE,
               "cache search call returned more than one result! "
               "DB Corrupted?\n");
@@ -465,8 +613,13 @@ int ifp_cache_check(struct ifp_user_get_attr_state *state,
     }
 
     if (state->res->count > 0) {
-        cache_expire = ldb_msg_find_attr_as_uint64(state->res->msgs[0],
-                                                   SYSDB_CACHE_EXPIRE, 0);
+        if (search_type == SSS_DP_USER) {
+            cache_expire = ldb_msg_find_attr_as_uint64(state->res->msgs[0],
+                                                       SYSDB_CACHE_EXPIRE, 0);
+        } else {
+            cache_expire = ldb_msg_find_attr_as_uint64(state->res->msgs[0],
+                                                       SYSDB_INITGR_EXPIRE, 0);
+        }
 
         /* if we have any reply let's check cache validity */
         ret = sss_cmd_check_cache(state->res->msgs[0], cache_refresh_percent,
@@ -493,7 +646,7 @@ int ifp_cache_check(struct ifp_user_get_attr_state *state,
         DEBUG(SSSDBG_TRACE_FUNC, "Performing midpoint cache update\n");
 
         req = sss_dp_get_account_send(state, state->rctx, state->dom, true,
-                                      SSS_DP_USER, state->inp, 0,
+                                      search_type, state->inp, 0,
                                       NULL);
         if (req == NULL) {
             DEBUG(SSSDBG_CRIT_FAILURE,
@@ -517,7 +670,7 @@ int ifp_cache_check(struct ifp_user_get_attr_state *state,
         state->check_provider = false;
 
         req = sss_dp_get_account_send(state, state->rctx, state->dom, true,
-                                      SSS_DP_USER, state->inp, 0, NULL);
+                                      search_type, state->inp, 0, NULL);
         if (req == NULL) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Out of memory sending data provider request\n");
-- 
1.9.0

