1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
#!/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 <jonstanley@gmail.com>
#
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:<scm_admin_group>. 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()
|