From ee2b081b9bb7421d6e9eabaa6be911d311fd322e Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 14 May 2014 13:03:00 +0100 Subject: [PATCH] Implement "pts membership" Signed-off-by: David Howells --- rpc-api/pts.xg | 5 ++ suite/commands/pts/g.py | 1 + suite/commands/pts/groups.py | 1 + suite/commands/pts/m.py | 1 + suite/commands/pts/membership.py | 147 +++++++++++++++++++++++++++++++ suite/lib/prcache.py | 94 +++++++++++++++++++- 6 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 suite/commands/pts/g.py create mode 100644 suite/commands/pts/groups.py create mode 100644 suite/commands/pts/m.py create mode 100644 suite/commands/pts/membership.py diff --git a/rpc-api/pts.xg b/rpc-api/pts.xg index 0173c37..4ee5e8f 100644 --- a/rpc-api/pts.xg +++ b/rpc-api/pts.xg @@ -295,4 +295,9 @@ ListEntries(IN uint32_t flags, IN uint32_t unknown, OUT prcheckentry *entries) = PRLISTENTRIES; +ListGroupsMemberOf(IN int32_t id, + OUT prlist *glist, + OUT uint32_t *unknown) = 530; + + /* the end */ diff --git a/suite/commands/pts/g.py b/suite/commands/pts/g.py new file mode 100644 index 0000000..c3eaaa3 --- /dev/null +++ b/suite/commands/pts/g.py @@ -0,0 +1 @@ +alias = "membership" diff --git a/suite/commands/pts/groups.py b/suite/commands/pts/groups.py new file mode 100644 index 0000000..c3eaaa3 --- /dev/null +++ b/suite/commands/pts/groups.py @@ -0,0 +1 @@ +alias = "membership" diff --git a/suite/commands/pts/m.py b/suite/commands/pts/m.py new file mode 100644 index 0000000..c3eaaa3 --- /dev/null +++ b/suite/commands/pts/m.py @@ -0,0 +1 @@ +alias = "membership" diff --git a/suite/commands/pts/membership.py b/suite/commands/pts/membership.py new file mode 100644 index 0000000..434db42 --- /dev/null +++ b/suite/commands/pts/membership.py @@ -0,0 +1,147 @@ +# +# AFS Server management toolkit: Find ownership of entry +# -*- coding: utf-8 -*- +# + +__copyright__ = """ +Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. +Written by David Howells (dhowells@redhat.com) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +""" + +from afs.argparse import * +from afs.lib.output import * +import kafs +import sys + +help = "Show the Protection Database groups owned by a user or group" + +command_arguments = [ + [ "nameorid", get_strings, "rm", "+" ], + [ "supergroups", get_dummy, "fn" ], + [ "expandgroups", get_dummy, "fn" ], + [ "cell", get_cell, "os", "" ], + [ "noauth", get_auth, "fn" ], + [ "localauth", get_auth, "fn" ], + [ "verbose", get_verbose, "fn" ], + [ "encrypt", get_dummy, "fn" ], + [ "force", get_dummy, "fn" ], +] + +cant_combine_arguments = [ + ( "cell", "localauth" ), + ( "noauth", "localauth" ), +] + +argument_size_limits = { + "nameorid" : kafs.PR_MAXNAMELEN, +} + +description = r""" +Show the Protection Database groups owned by a user or group +""" + +def main(params): + cell = params["cell"] + prcache = cell.get_prcache(params) + + for name in params["nameorid"]: + prcache.precache_name_or_id(name) + + requests = [] + memberships = dict() + + for name in params["nameorid"]: + gid = prcache.name_or_id_to_id(name) + if gid == None: + error("User or group doesn't exist so couldn't look up id for " + name + "\n") + if "force" not in params: + break + continue + + if gid not in requests: + try: + if gid < 0: + # Group + verbose("Listing membership of ", gid, " (", name, ")\n") + group = prcache.id_to_group(gid) + prcache.precache_ids(group) + else: + # User - ListElements returns the ancestry of a non-group + ret = cell.call_pt_server(params, kafs.PR_ListElements, gid) + elist = ret.elist + memberships[gid] = elist + prcache.precache_ids(elist) + for i in elist: + if i < 0: + prcache.id_to_group(i) + requests.append(gid) + + except kafs.AbortPRNOENT: + error("User or group doesn't exist ", name, " (id ", gid, ")\n") + prcache.id_is_unknown(gid) + if "force" not in params: + break + except kafs.AbortPRPERM: + error("Permission denied on ID ", name, " (id: ", gid, ")\n") + prcache.id_is_unknown(gid) + if "force" not in params: + break + + if "expandgroups" in params: + groups_needing_expansion = set(prcache.known_groups()) + verbose("Expand groups ", groups_needing_expansion, "\n") + while groups_needing_expansion: + gid = groups_needing_expansion.pop() + members = prcache.id_to_group(gid) + for m in members: + prcache.precache_id(m) + if m < 0 and not prcache.have_group(m) and m not in groups_needing_expansion: + groups_needing_expansion.add(m) + + if "supergroups" in params: + for r in requests: + if r < 0: + ret = cell.call_pt_server(params, kafs.PR_ListGroupsMemberOf, r) + glist = ret.glist + memberships[r] = glist + + for r in requests: + # Display members of a group + if r < 0: + if "expandgroups" in params: + output("Expanded Members of ", prcache.id_to_name(r), " (id: ", r, ") are:\n") + for m in prcache.id_to_expanded_group(r): + if m > 0: + output(" ", prcache.id_to_name(m), "\n") + else: + output("Members of ", prcache.id_to_name(r), " (id: ", r, ") are:\n") + for m in prcache.id_to_group(r): + output(" ", prcache.id_to_name(m), "\n") + + # Display membership of a user or a group + if r > 0 and "expandgroups" in params: + output("Expanded Groups ", prcache.id_to_name(r), " (id: ", r, ") is a member of:\n") + member_of = memberships[r] + expanded = set(member_of) + for gid in member_of: + expanded |= prcache.id_to_expanded_group(gid) + for m in expanded: + if m < 0: + output(" ", prcache.id_to_name(m), "\n") + elif r > 0 or "supergroups" in params: + output("Groups ", prcache.id_to_name(r), " (id: ", r, ") is a member of:\n") + for gid in memberships[r]: + output(" ", prcache.id_to_name(gid), "\n") diff --git a/suite/lib/prcache.py b/suite/lib/prcache.py index d5f702d..da62e43 100644 --- a/suite/lib/prcache.py +++ b/suite/lib/prcache.py @@ -37,6 +37,37 @@ class AFS_PR_Entry: def ugid(self): return self.__id +class pr_group: + """Protection Database group list""" + def __init__(self, gid, members): + self.__gid = gid + self.__members = members + self.__expanded_members = None + + def gid(self): + return self.__gid + + def members(self): + return self.__members + + def expand(self, prcache): + if self.__expanded_members == None: + self.__expanded_members = frozenset() # Prevent recursion loops + + expansion = set() + xgroups = set() + for uid in self.__members: + expansion.add(uid) + if uid < 0: + xgroups.add(uid) + + for gid in frozenset(xgroups): + expansion |= prcache.id_to_group_object(gid).expand(prcache) + + self.__expanded_members = frozenset(expansion) + verbose("Expanded ", self.__gid, " to ", self.__expanded_members, "\n") + return self.__expanded_members + class prcache: """Protection Database Name/ID mapping cache for a cell""" def __init__(self, params, cell): @@ -46,6 +77,8 @@ class prcache: self.__id2name = dict() self.__names_to_lookup = [] self.__ids_to_lookup = [] + self.__groups = dict() + self.__groups_to_lookup = [] ########################################################################### # @@ -127,13 +160,17 @@ class prcache: ########################################################################### # - # Precache a ID + # Precache an ID or IDS # ########################################################################### def precache_id(self, ugid): if ugid not in self.__ids_to_lookup and ugid not in self.__id2name: self.__ids_to_lookup.append(ugid) + def precache_ids(self, ugids): + for ugid in ugids: + self.precache_id(ugid) + ########################################################################### # # Precache a name or a stringified ID @@ -200,3 +237,58 @@ class prcache: if ugid not in self.__id2name: entry = AFS_PR_Entry(None, ugid) self.__id2name[ugid] = entry + + ########################################################################### + # + # Do the pending groups lookups + # + ########################################################################### + def do_look_up_group(self, gid): + verbose("Look up group ", gid, "\n") + assert(gid < 0) + ret = self.__cell.call_pt_server(self.__params, kafs.PR_ListElements, gid) + entries = ret.elist + self.__groups[gid] = pr_group(gid, entries) + + ########################################################################### + # + # Look up a group by ID + # + ########################################################################### + def id_to_group_object(self, gid): + assert(gid < 0) + if gid not in self.__groups: + self.do_look_up_group(gid) + return self.__groups[gid] + + def id_to_group(self, gid): + assert(gid < 0) + return self.id_to_group_object(gid).members() + + ########################################################################### + # + # Query if we have a group yet + # + ########################################################################### + def have_group(self, gid): + assert(gid < 0) + return gid in self.__groups + + ########################################################################### + # + # Get list of known groups + # + ########################################################################### + def known_groups(self): + return self.__groups.keys() + + ########################################################################### + # + # Return the membership of a group, expanded to recursively turn all + # subgroups into their constituent members + # + ########################################################################### + def id_to_expanded_group(self, gid): + assert(gid < 0) + group = self.__groups[gid] + return group.expand(self) -- 2.50.1