#!/usr/bin/python # Copyright 2009 Jon Stanley # # This copyrighted material is made available to anyone wishing to use, modify, # copy, or redistribute it subject to the terms and conditions of the GNU # General Public License v.2. This program is distributed in the hope that it # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. You should have # received a copy of the GNU General Public License along with this program; # if not, write to the Free Software Foundation, Inc., 51 Franklin Street, # Fifth Floor, Boston, MA 02110-1301, USA. # # Author(s): Jon Stanley # import sys import posix1e import re import grp import pwd import os from optparse import OptionParser CVSBASE = '/cvs/pkgs' GROUP = re.compile('^@') # this will become the group owner of all of the directories scm_admin_group = 'cvsadmin' # these groups can commit to anything, we'll just add them to the ACL like any # other groups like provenpackager secondary_arch_groups = ['fedora-s390', 'fedora-ia64', 'fedora-sparc'] version = '0.1' try: avail_file = open('avail.pkgs','r') except IOError: sys.stderr.write('Cannot open avail file!\n') sys.exit(1) pkgs = {} def get_one(pkg): '''Returns applicable ACL's for a given package. Supports multiple matches, returns a dict of paths that we want to set and ACL's''' pkg_ret = {} item_regex = re.compile('rpms/%s.*' % pkg) for item in pkgs.keys(): if item_regex.match(item): pkg_ret[item] = build_acl(pkgs[item]) return pkg_ret def get_all(): '''Returns all packages in a dict of ACL objects. The keys of the dict are paths of the branches that we want to set ACL's on''' acls = {} for line in pkgs.keys(): acl_list = pkgs[line] acls[line] = build_acl(acl_list) print acls for acl in acls.keys(): print acls[acl] def build_acl(acl_text): '''Builds an ACL from a line in the avail file. Expects a list of users and groups (groups should be prepended with an @), and returns an ACL object. This will also add the secondary arch groups to the directories''' for item in acl_text: if GROUP.match(item): try: groups.append(item.lstrip('@')) except NameError: groups = [item.lstrip('@')] else: try: people.append(item) except NameError: people = [item] acl = posix1e.ACL() user = posix1e.Entry(acl) user.tag_type = posix1e.ACL_USER_OBJ user.permset.add(posix1e.ACL_READ | posix1e.ACL_WRITE | posix1e.ACL_EXECUTE) group = posix1e.Entry(acl) group.copy(user) group.tag_type=posix1e.ACL_GROUP_OBJ other = posix1e.Entry(acl) other.tag_type=posix1e.ACL_OTHER other.permset.add(posix1e.ACL_READ | posix1e.ACL_EXECUTE) mask = posix1e.Entry(acl) mask.tag_type=posix1e.ACL_MASK mask.permset.add(posix1e.ACL_READ | posix1e.ACL_WRITE | posix1e.ACL_EXECUTE) for item in groups: group_acl = posix1e.Entry(acl) group_acl.tag_type = posix1e.ACL_GROUP group_acl.qualifier = grp.getgrnam(item).gr_gid group_acl.permset.add(posix1e.ACL_READ | posix1e.ACL_WRITE | posix1e.ACL_EXECUTE) for item in secondary_arch_groups: group_acl = posix1e.Entry(acl) group_acl.tag_type = posix1e.ACL_GROUP group_acl.qualifier = grp.getgrnam(item).gr_gid group_acl.permset.add(posix1e.ACL_READ | posix1e.ACL_WRITE | posix1e.ACL_EXECUTE) for item in people: people_acl = posix1e.Entry(acl) people_acl.tag_type = posix1e.ACL_USER people_acl.qualifier = pwd.getpwnam(item).pw_uid people_acl.permset.add(posix1e.ACL_READ | posix1e.ACL_WRITE | posix1e.ACL_EXECUTE) return acl def apply_acls(acl_dict, dry_run=False): '''Applies ACL's to the filesystem. Take a dictionary of ACL's, with the keys being the path that you would like to ACL's on. The global variable CVSBASE will be prepended to the keys of the dict. Ownership of the directory on completion will be root:. The ACL sets the directories to be group writable, so the admin group will be able to write to the directories, in addition to anyone specifically authorized''' scm_admin_gid = grp.getgrnam(scm_admin_group).gr_gid for dir in acl_dict.keys(): real_dir = os.path.join(CVSBASE, dir) if not dry_run: acl_dict[dir].applyto(real_dir, posix1e.ACL_TYPE_DEFAULT) acl_dict[dir].applyto(real_dir, posix1e.ACL_TYPE_ACCESS) os.chown(real_dir, 0, scm_admin_gid) else: print 'would apply the following ACL to %s' % real_dir print acl_dict[dir] for file in os.listdir(real_dir): real_file = os.path.join(real_dir, file) if not dry_run: if os.path.isdir(real_file): acl_dict[dir].applyto(real_file, posix1e.ACL_TYPE_DEFAULT) acl_dict[dir].applyto(real_file, posix1e.ACL_TYPE_ACCESS) os.chown(real_file, 0, scm_admin_gid) if os.path.isfile(real_file): acl_dict[dir].applyto(real_file, posix1e.ACL_TYPE_ACCESS) os.chown(real_file, 0, scm_admin_gid) else: print 'would apply the following ACL to %s' % real_file print acl_dict[dir] def main(): usage = '%prog [options] [pkgs...]' parser = OptionParser(usage, version=version) parser.add_option('-a', '--all-packages', action='store_true', dest='all', help='Operate on all packages (slow and destructive') parser.add_option('-D', '--debug', action='store_true', dest='debug', help= 'Print extra debug info') parser.add_option('-d', '--dry-run', action='store_true', dest='dr', help= 'Just print what would be done') options, args = parser.parse_args() # read the avail file into memory to make things faster for line in avail_file.readlines(): parsed, pkg = line.strip().split(' | ')[1].split(','), \ line.strip().split(' | ')[2] pkgs[pkg] = parsed if not options.all and not args: sys.stderr.write('either the -a option or some packages must be given\n') sys.exit(1) if options.all and args: sys.stderr.write('You cannot specify both -a and a list of packages\n') sys.exit(1) # something of a hack here - this list will only ever have one element. # This is for support of multiple command line arguments and making sure # that what's around is always a list. See below for when it can have more # than one if options.all: acl_list = [] acl_list.append = get_all() if options.dr: dry_run = True else: dry_run = False if args: acl_list = [] for arg in args: acl_list.append(get_one(arg)) for acl_dict in acl_list: apply_acls(acl_dict, dry_run) if __name__ == '__main__': main()