diff options
Diffstat (limited to 'src/responder/ifp')
-rw-r--r-- | src/responder/ifp/ifp_groups.c | 521 | ||||
-rw-r--r-- | src/responder/ifp/ifp_groups.h | 23 | ||||
-rw-r--r-- | src/responder/ifp/ifp_iface.c | 10 | ||||
-rw-r--r-- | src/responder/ifp/ifp_iface.xml | 11 | ||||
-rw-r--r-- | src/responder/ifp/ifp_iface_generated.c | 68 | ||||
-rw-r--r-- | src/responder/ifp/ifp_iface_generated.h | 24 | ||||
-rw-r--r-- | src/responder/ifp/org.freedesktop.sssd.infopipe.conf | 1 |
7 files changed, 658 insertions, 0 deletions
diff --git a/src/responder/ifp/ifp_groups.c b/src/responder/ifp/ifp_groups.c index e362002d3..f1f86ce1a 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 (subreq == 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 b93757326..0474ca44f 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 e762ffa28..ce987b607 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 31fb529fe..fbd3d3f5a 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 355db6e84..ff00be8ea 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 09db24b73..1bd2a448f 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 916218c8b..b8df49350 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"> |