>From f3f4712e537ed6bf0758852dd402d3a2e7b77873 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Tue, 10 Feb 2015 12:19:19 +0100
Subject: [PATCH 7/8] IFP: add org.freedesktop.sssd.infopipe.Groups.Group

Example calls:

dbus-send --print-reply --system --dest=org.freedesktop.sssd.infopipe /org/freedesktop/sssd/infopipe/Groups/LDAP_2ePB/30001 org.freedesktop.DBus.Properties.Get string:org.freedesktop.sssd.infopipe.Groups.Group string:name
method return sender=:1.159 -> dest=:1.168 reply_serial=2
   variant       string "group-1"

dbus-send --print-reply --system --dest=org.freedesktop.sssd.infopipe /org/freedesktop/sssd/infopipe/Groups/LDAP/30002 org.freedesktop.sssd.infopipe.Groups.Group.UpdateMemberList
method return sender=:1.934 -> dest=:1.937 reply_serial=2

Resolves:
https://fedorahosted.org/sssd/ticket/2150
---
 src/responder/ifp/ifp_groups.c                     | 521 +++++++++++++++++++++
 src/responder/ifp/ifp_groups.h                     |  23 +
 src/responder/ifp/ifp_iface.c                      |  10 +
 src/responder/ifp/ifp_iface.xml                    |  11 +
 src/responder/ifp/ifp_iface_generated.c            |  68 +++
 src/responder/ifp/ifp_iface_generated.h            |  24 +
 .../ifp/org.freedesktop.sssd.infopipe.conf         |   1 +
 7 files changed, 658 insertions(+)

diff --git a/src/responder/ifp/ifp_groups.c b/src/responder/ifp/ifp_groups.c
index e362002d35b6aff6f35f71c0dfc37015a2fc89e5..9fca756ffba6f8f999450e8ef1976d7d9e68d9e2 100644
--- a/src/responder/ifp/ifp_groups.c
+++ b/src/responder/ifp/ifp_groups.c
@@ -23,10 +23,12 @@
 
 #include "util/util.h"
 #include "db/sysdb.h"
+#include "util/strtonum.h"
 #include "sbus/sssd_dbus_errors.h"
 #include "responder/common/responder.h"
 #include "responder/common/responder_cache_req.h"
 #include "responder/ifp/ifp_groups.h"
+#include "responder/ifp/ifp_users.h"
 
 char * ifp_groups_build_path_from_msg(TALLOC_CTX *mem_ctx,
                                       struct sss_domain_info *domain,
@@ -43,6 +45,41 @@ char * ifp_groups_build_path_from_msg(TALLOC_CTX *mem_ctx,
     return sbus_opath_compose(mem_ctx, IFP_PATH_GROUPS, domain->name, gid);
 }
 
+static errno_t ifp_groups_decompose_path(struct sss_domain_info *domains,
+                                         const char *path,
+                                         struct sss_domain_info **_domain,
+                                         gid_t *_gid)
+{
+    char **parts = NULL;
+    struct sss_domain_info *domain;
+    gid_t gid;
+    errno_t ret;
+
+    ret = sbus_opath_decompose_exact(NULL, path, IFP_PATH_GROUPS, 2, &parts);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    domain = find_domain_by_name(domains, parts[0], false);
+    if (domain == NULL) {
+        ret = ERR_DOMAIN_NOT_FOUND;
+        goto done;
+    }
+
+    gid = strtouint32(parts[1], NULL, 10);
+    if (errno != 0) {
+        ret = errno;
+        goto done;
+    }
+
+    *_domain = domain;
+    *_gid = gid;
+
+done:
+    talloc_free(parts);
+    return ret;
+}
+
 static void ifp_groups_find_by_name_done(struct tevent_req *req);
 
 int ifp_groups_find_by_name(struct sbus_request *sbus_req,
@@ -199,3 +236,487 @@ int ifp_groups_list_by_domain_and_name(struct sbus_request *sbus_req,
 {
     return EOK;
 }
+
+static errno_t
+ifp_groups_group_get(struct sbus_request *sbus_req,
+                     void *data,
+                     gid_t *_gid,
+                     struct sss_domain_info **_domain,
+                     struct ldb_message **_group)
+{
+    struct ifp_ctx *ctx;
+    struct sss_domain_info *domain;
+    struct ldb_result *res;
+    uid_t gid;
+    errno_t ret;
+
+    ctx = talloc_get_type(data, struct ifp_ctx);
+    if (ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
+        return ERR_INTERNAL;
+    }
+
+    ret = ifp_groups_decompose_path(ctx->rctx->domains, sbus_req->path,
+                                    &domain, &gid);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to decompose object path"
+              "[%s] [%d]: %s\n", sbus_req->path, ret, sss_strerror(ret));
+        return ret;
+    }
+
+    if (_group != NULL) {
+        ret = sysdb_getgrgid_with_views(sbus_req, domain, gid, &res);
+        if (ret == EOK && res->count == 0) {
+            *_group = NULL;
+            ret = ENOENT;
+        }
+
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to lookup group %u@%s [%d]: %s\n",
+                  gid, domain->name, ret, sss_strerror(ret));
+        } else {
+            *_group = res->msgs[0];
+        }
+    }
+
+    if (ret == EOK || ret == ENOENT) {
+        if (_gid != NULL) {
+            *_gid = gid;
+        }
+
+        if (_domain != NULL) {
+            *_domain = domain;
+        }
+    }
+
+    return ret;
+}
+
+struct resolv_ghosts_state {
+    struct tevent_context *ev;
+    struct sbus_request *sbus_req;
+    struct ifp_ctx *ctx;
+    void *data;
+
+    struct sss_domain_info *domain;
+    const char **ghosts;
+    int index;
+};
+
+static void resolv_ghosts_group_done(struct tevent_req *subreq);
+static errno_t resolv_ghosts_step(struct tevent_req *req);
+static void resolv_ghosts_done(struct tevent_req *subreq);
+
+static struct tevent_req *resolv_ghosts_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct sbus_request *sbus_req,
+                                             void *data)
+{
+    struct resolv_ghosts_state *state;
+    struct sss_domain_info *domain;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct ldb_message *group;
+    struct ifp_ctx *ctx;
+    const char *name;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct resolv_ghosts_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
+    }
+
+    ctx = talloc_get_type(data, struct ifp_ctx);
+    if (ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
+        ret = ERR_INTERNAL;
+        goto immediately;
+    }
+
+    state->ev = ev;
+    state->sbus_req = sbus_req;
+    state->ctx = ctx;
+    state->data = data;
+
+    ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group);
+    if (ret != EOK) {
+        goto immediately;
+    }
+
+    name = ldb_msg_find_attr_as_string(group, SYSDB_NAME, NULL);
+    if (name == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Group name is empty!\n");
+        ret = ERR_INTERNAL;
+        goto immediately;
+    }
+
+    subreq = cache_req_group_by_name_send(state, ev, ctx->rctx,
+                                          ctx->ncache, ctx->neg_timeout, 0,
+                                          domain->name, name);
+    if (req == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    tevent_req_set_callback(subreq, resolv_ghosts_group_done, req);
+
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static void resolv_ghosts_group_done(struct tevent_req *subreq)
+{
+    struct resolv_ghosts_state *state;
+    struct ldb_message_element *el;
+    struct ldb_message *group;
+    struct tevent_req *req;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct resolv_ghosts_state);
+
+    ret = ifp_groups_group_get(state->sbus_req, state->data, NULL,
+                               &state->domain, &group);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    el = ldb_msg_find_element(group, SYSDB_GHOST);
+    if (el == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    if (el->num_values == 0) {
+        ret = EOK;
+        goto done;
+    }
+
+    state->ghosts = sss_ldb_el_to_string_list(state, el);
+    if (state->ghosts == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    state->index = 0;
+    ret = resolv_ghosts_step(req);
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else if (ret != EAGAIN) {
+        tevent_req_error(req, ret);
+    }
+}
+
+errno_t resolv_ghosts_step(struct tevent_req *req)
+{
+    struct resolv_ghosts_state *state;
+    struct tevent_req *subreq;
+
+    state = tevent_req_data(req, struct resolv_ghosts_state);
+
+    if (state->ghosts[state->index] == NULL) {
+        return EOK;
+    }
+
+    subreq = cache_req_user_by_name_send(state, state->ev, state->ctx->rctx,
+                                   state->ctx->ncache, state->ctx->neg_timeout,
+                                   0, state->domain->name,
+                                   state->ghosts[state->index]);
+    if (subreq == NULL) {
+        return ENOMEM;
+    }
+
+    tevent_req_set_callback(subreq, resolv_ghosts_done, req);
+
+    state->index++;
+
+    return EAGAIN;
+}
+
+static void resolv_ghosts_done(struct tevent_req *subreq)
+{
+    struct resolv_ghosts_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct resolv_ghosts_state);
+
+    ret = cache_req_user_by_name_recv(state, subreq, NULL, NULL, NULL);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = resolv_ghosts_step(req);
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else if (ret != EAGAIN) {
+        tevent_req_error(req, ret);
+    }
+}
+
+static errno_t resolv_ghosts_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+static void ifp_groups_group_update_member_list_done(struct tevent_req *req);
+
+int ifp_groups_group_update_member_list(struct sbus_request *sbus_req,
+                                        void *data)
+{
+    struct tevent_req *subreq;
+    struct ifp_ctx *ctx;
+
+    ctx = talloc_get_type(data, struct ifp_ctx);
+    if (ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
+        return ERR_INTERNAL;
+    }
+
+    subreq = resolv_ghosts_send(sbus_req, ctx->rctx->ev, sbus_req, data);
+    if (subreq == NULL) {
+        return ENOMEM;
+    }
+
+    tevent_req_set_callback(subreq, ifp_groups_group_update_member_list_done,
+                            sbus_req);
+
+    return EOK;
+}
+
+static void ifp_groups_group_update_member_list_done(struct tevent_req *subreq)
+{
+    DBusError *error;
+    struct sbus_request *sbus_req;
+    errno_t ret;
+
+    sbus_req = tevent_req_callback_data(subreq, struct sbus_request);
+
+    ret = resolv_ghosts_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED,
+                               "Unable to resolve ghost members [%d]: %s\n",
+                               ret, sss_strerror(ret));
+        sbus_request_fail_and_finish(sbus_req, error);
+        return;
+    }
+
+    iface_ifp_groups_group_UpdateMemberList_finish(sbus_req);
+    return;
+}
+
+void ifp_groups_group_get_name(struct sbus_request *sbus_req,
+                               void *data,
+                               const char **_out)
+{
+    struct ldb_message *msg;
+    struct sss_domain_info *domain;
+    errno_t ret;
+
+    ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &msg);
+    if (ret != EOK) {
+        *_out = NULL;
+        return;
+    }
+
+    *_out = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_NAME, NULL);
+
+    return;
+}
+
+void ifp_groups_group_get_gid_number(struct sbus_request *sbus_req,
+                                     void *data,
+                                     uint32_t *_out)
+{
+    struct ldb_message *msg;
+    struct sss_domain_info *domain;
+    errno_t ret;
+
+    ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &msg);
+    if (ret != EOK) {
+        *_out = 0;
+        return;
+    }
+
+    *_out = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM, 0);
+
+    return;
+}
+
+static errno_t
+ifp_groups_group_get_members(TALLOC_CTX *mem_ctx,
+                             struct sbus_request *sbus_req,
+                             void *data,
+                             const char ***_users,
+                             int *_num_users,
+                             const char ***_groups,
+                             int *_num_groups)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct sss_domain_info *domain;
+    struct ldb_message *group;
+    struct ldb_message **members;
+    size_t num_members;
+    const char *class;
+    const char **users;
+    const char **groups;
+    int num_users;
+    int num_groups;
+    int i;
+    errno_t ret;
+    const char *attrs[] = {SYSDB_OBJECTCLASS, SYSDB_UIDNUM,
+                           SYSDB_GIDNUM, NULL};
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    ret = ifp_groups_group_get(sbus_req, data, NULL, &domain, &group);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = sysdb_asq_search(tmp_ctx, domain, group->dn, NULL, SYSDB_MEMBER,
+                           attrs, &num_members, &members);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to perform ASQ search [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    if (num_members == 0) {
+        users = NULL;
+        groups = NULL;
+        num_users = 0;
+        num_groups = 0;
+        ret = EOK;
+        goto done;
+    }
+
+    users = talloc_zero_array(tmp_ctx, const char *, num_members + 1);
+    if (users == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    groups = talloc_zero_array(tmp_ctx, const char *, num_members + 1);
+    if (groups == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    num_users = 0;
+    num_groups = 0;
+    for (i = 0; i < num_members; i++) {
+        class = ldb_msg_find_attr_as_string(members[i], SYSDB_OBJECTCLASS,
+                                            NULL);
+        if (class == NULL) {
+            ret = ERR_INTERNAL;
+            goto done;
+        }
+
+        if (strcmp(class, SYSDB_USER_CLASS) == 0) {
+            users[num_users] = ifp_users_build_path_from_msg(users, domain,
+                                                             members[i]);
+            if (users[num_users] == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+
+            num_users++;
+        } else if (strcmp(class, SYSDB_GROUP_CLASS) == 0) {
+            groups[num_groups] = ifp_groups_build_path_from_msg(groups,
+                                                         domain, members[i]);
+            if (groups[num_groups] == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+
+            num_groups++;
+        } else {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected object class %s\n", class);
+            ret = ERR_INTERNAL;
+            goto done;
+        }
+    }
+
+    ret = EOK;
+
+done:
+    if (ret == EOK) {
+        if (_users != NULL) {
+            *_users = talloc_steal(mem_ctx, users);
+        }
+
+        if (_num_users != NULL) {
+            *_num_users = num_users;
+        }
+
+        if (_groups != NULL) {
+            *_groups = talloc_steal(mem_ctx, groups);
+        }
+
+        if (_num_groups != NULL) {
+            *_num_groups = num_groups;
+        }
+    }
+
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+void ifp_groups_group_get_users(struct sbus_request *sbus_req,
+                                void *data,
+                                const char ***_out,
+                                int *_size)
+{
+    errno_t ret;
+
+    *_out = NULL;
+    *_size = 0;
+
+    ret = ifp_groups_group_get_members(sbus_req, sbus_req, data, _out, _size,
+                                       NULL, NULL);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to acquire groups members\n");
+    }
+}
+
+void ifp_groups_group_get_groups(struct sbus_request *sbus_req,
+                                void *data,
+                                const char ***_out,
+                                int *_size)
+{
+    errno_t ret;
+
+    *_out = NULL;
+    *_size = 0;
+
+    ret = ifp_groups_group_get_members(sbus_req, sbus_req, data, NULL, NULL,
+                                       _out, _size);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to acquire groups members\n");
+    }
+}
diff --git a/src/responder/ifp/ifp_groups.h b/src/responder/ifp/ifp_groups.h
index b93757326d2db31f80ffc44797cd70fbcd97995f..0474ca44f19cb847ed803d0681f5aac19e55be4a 100644
--- a/src/responder/ifp/ifp_groups.h
+++ b/src/responder/ifp/ifp_groups.h
@@ -54,4 +54,27 @@ int ifp_groups_list_by_domain_and_name(struct sbus_request *sbus_req,
                                        const char *filter,
                                        uint32_t limit);
 
+/* org.freedesktop.sssd.infopipe.Groups.Group */
+
+int ifp_groups_group_update_member_list(struct sbus_request *sbus_req,
+                                        void *data);
+
+void ifp_groups_group_get_name(struct sbus_request *sbus_req,
+                               void *data,
+                               const char **_out);
+
+void ifp_groups_group_get_gid_number(struct sbus_request *sbus_req,
+                                     void *data,
+                                     uint32_t *_out);
+
+void ifp_groups_group_get_users(struct sbus_request *sbus_req,
+                                void *data,
+                                const char ***_out,
+                                int *_size);
+
+void ifp_groups_group_get_groups(struct sbus_request *sbus_req,
+                                void *data,
+                                const char ***_out,
+                                int *_size);
+
 #endif /* IFP_GROUPS_H_ */
diff --git a/src/responder/ifp/ifp_iface.c b/src/responder/ifp/ifp_iface.c
index e762ffa2833a0cf43ba0a96a23054e599a52ba74..ce987b6075ee886e7608455600e0a788c30c9741 100644
--- a/src/responder/ifp/ifp_iface.c
+++ b/src/responder/ifp/ifp_iface.c
@@ -107,6 +107,15 @@ struct iface_ifp_groups iface_ifp_groups = {
     .ListByDomainAndName = ifp_groups_list_by_domain_and_name
 };
 
+struct iface_ifp_groups_group iface_ifp_groups_group = {
+    { &iface_ifp_groups_group_meta, 0 },
+    .UpdateMemberList = ifp_groups_group_update_member_list,
+    .get_name = ifp_groups_group_get_name,
+    .get_gidNumber = ifp_groups_group_get_gid_number,
+    .get_users = ifp_groups_group_get_users,
+    .get_groups = ifp_groups_group_get_groups
+};
+
 struct iface_map {
     const char *path;
     struct sbus_vtable *vtable;
@@ -119,6 +128,7 @@ static struct iface_map iface_map[] = {
     { IFP_PATH_USERS, &iface_ifp_users.vtable },
     { IFP_PATH_USERS_TREE, &iface_ifp_users_user.vtable },
     { IFP_PATH_GROUPS, &iface_ifp_groups.vtable },
+    { IFP_PATH_GROUPS_TREE, &iface_ifp_groups_group.vtable },
     { NULL, NULL },
 };
 
diff --git a/src/responder/ifp/ifp_iface.xml b/src/responder/ifp/ifp_iface.xml
index 31fb529fe650d1f2776ca648943acab49be10914..fbd3d3f5ac812a21c8768e28188217edbc83300b 100644
--- a/src/responder/ifp/ifp_iface.xml
+++ b/src/responder/ifp/ifp_iface.xml
@@ -164,4 +164,15 @@
             <arg name="result" type="ao" direction="out"/>
         </method>
     </interface>
+
+    <interface name="org.freedesktop.sssd.infopipe.Groups.Group">
+        <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="iface_ifp_groups_group"/>
+
+        <method name="UpdateMemberList" />
+
+        <property name="name" type="s" access="read" />
+        <property name="gidNumber" type="u" access="read" />
+        <property name="users" type="ao" access="read" />
+        <property name="groups" type="ao" access="read" />
+    </interface>
 </node>
diff --git a/src/responder/ifp/ifp_iface_generated.c b/src/responder/ifp/ifp_iface_generated.c
index 355db6e84d7ee439bde0a23656c9600da4005778..ff00be8eaaedc28e37d39cdc85262c8041dafa0b 100644
--- a/src/responder/ifp/ifp_iface_generated.c
+++ b/src/responder/ifp/ifp_iface_generated.c
@@ -877,6 +877,74 @@ const struct sbus_interface_meta iface_ifp_groups_meta = {
     sbus_invoke_get_all, /* GetAll invoker */
 };
 
+int iface_ifp_groups_group_UpdateMemberList_finish(struct sbus_request *req)
+{
+   return sbus_request_return_and_finish(req,
+                                         DBUS_TYPE_INVALID);
+}
+
+/* methods for org.freedesktop.sssd.infopipe.Groups.Group */
+const struct sbus_method_meta iface_ifp_groups_group__methods[] = {
+    {
+        "UpdateMemberList", /* name */
+        NULL, /* no in_args */
+        NULL, /* no out_args */
+        offsetof(struct iface_ifp_groups_group, UpdateMemberList),
+        NULL, /* no invoker */
+    },
+    { NULL, }
+};
+
+/* property info for org.freedesktop.sssd.infopipe.Groups.Group */
+const struct sbus_property_meta iface_ifp_groups_group__properties[] = {
+    {
+        "name", /* name */
+        "s", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct iface_ifp_groups_group, get_name),
+        sbus_invoke_get_s,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "gidNumber", /* name */
+        "u", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct iface_ifp_groups_group, get_gidNumber),
+        sbus_invoke_get_u,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "users", /* name */
+        "ao", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct iface_ifp_groups_group, get_users),
+        sbus_invoke_get_ao,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    {
+        "groups", /* name */
+        "ao", /* type */
+        SBUS_PROPERTY_READABLE,
+        offsetof(struct iface_ifp_groups_group, get_groups),
+        sbus_invoke_get_ao,
+        0, /* not writable */
+        NULL, /* no invoker */
+    },
+    { NULL, }
+};
+
+/* interface info for org.freedesktop.sssd.infopipe.Groups.Group */
+const struct sbus_interface_meta iface_ifp_groups_group_meta = {
+    "org.freedesktop.sssd.infopipe.Groups.Group", /* name */
+    iface_ifp_groups_group__methods,
+    NULL, /* no signals */
+    iface_ifp_groups_group__properties,
+    sbus_invoke_get_all, /* GetAll invoker */
+};
+
 /* invokes a handler with a 'ssu' DBus signature */
 static int invoke_ssu_method(struct sbus_request *dbus_req, void *function_ptr)
 {
diff --git a/src/responder/ifp/ifp_iface_generated.h b/src/responder/ifp/ifp_iface_generated.h
index 09db24b7319fbb3823dd742233e9dd55965511c1..1bd2a448f3ee9af73091bd991a27f6b60f07a579 100644
--- a/src/responder/ifp/ifp_iface_generated.h
+++ b/src/responder/ifp/ifp_iface_generated.h
@@ -80,6 +80,14 @@
 #define IFACE_IFP_GROUPS_LISTBYNAME "ListByName"
 #define IFACE_IFP_GROUPS_LISTBYDOMAINANDNAME "ListByDomainAndName"
 
+/* constants for org.freedesktop.sssd.infopipe.Groups.Group */
+#define IFACE_IFP_GROUPS_GROUP "org.freedesktop.sssd.infopipe.Groups.Group"
+#define IFACE_IFP_GROUPS_GROUP_UPDATEMEMBERLIST "UpdateMemberList"
+#define IFACE_IFP_GROUPS_GROUP_NAME "name"
+#define IFACE_IFP_GROUPS_GROUP_GIDNUMBER "gidNumber"
+#define IFACE_IFP_GROUPS_GROUP_USERS "users"
+#define IFACE_IFP_GROUPS_GROUP_GROUPS "groups"
+
 /* ------------------------------------------------------------------------
  * DBus handlers
  *
@@ -245,6 +253,19 @@ int iface_ifp_groups_ListByName_finish(struct sbus_request *req, const char *arg
 /* finish function for ListByDomainAndName */
 int iface_ifp_groups_ListByDomainAndName_finish(struct sbus_request *req, const char *arg_result[], int len_result);
 
+/* vtable for org.freedesktop.sssd.infopipe.Groups.Group */
+struct iface_ifp_groups_group {
+    struct sbus_vtable vtable; /* derive from sbus_vtable */
+    int (*UpdateMemberList)(struct sbus_request *req, void *data);
+    void (*get_name)(struct sbus_request *, void *data, const char **);
+    void (*get_gidNumber)(struct sbus_request *, void *data, uint32_t*);
+    void (*get_users)(struct sbus_request *, void *data, const char ***, int *);
+    void (*get_groups)(struct sbus_request *, void *data, const char ***, int *);
+};
+
+/* finish function for UpdateMemberList */
+int iface_ifp_groups_group_UpdateMemberList_finish(struct sbus_request *req);
+
 /* ------------------------------------------------------------------------
  * DBus Interface Metadata
  *
@@ -273,4 +294,7 @@ extern const struct sbus_interface_meta iface_ifp_users_user_meta;
 /* interface info for org.freedesktop.sssd.infopipe.Groups */
 extern const struct sbus_interface_meta iface_ifp_groups_meta;
 
+/* interface info for org.freedesktop.sssd.infopipe.Groups.Group */
+extern const struct sbus_interface_meta iface_ifp_groups_group_meta;
+
 #endif /* __IFP_IFACE_XML__ */
diff --git a/src/responder/ifp/org.freedesktop.sssd.infopipe.conf b/src/responder/ifp/org.freedesktop.sssd.infopipe.conf
index 916218c8b70d3989a0e619d4f50080c0dd9521bd..b8df49350ecdd119fdde196a21771cae41dc52ef 100644
--- a/src/responder/ifp/org.freedesktop.sssd.infopipe.conf
+++ b/src/responder/ifp/org.freedesktop.sssd.infopipe.conf
@@ -33,6 +33,7 @@
     <allow send_interface="org.freedesktop.sssd.infopipe.Users"/>
     <allow send_interface="org.freedesktop.sssd.infopipe.Users.User"/>
     <allow send_interface="org.freedesktop.sssd.infopipe.Groups"/>
+    <allow send_interface="org.freedesktop.sssd.infopipe.Groups.Group"/>
   </policy>
 
   <policy user="root">
-- 
1.9.3

