Child: [bea16d] (diff)

Download this file

007-update-acls.py    206 lines (187 with data), 7.5 kB

  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
import logging
from optparse import OptionParser
from pprint import pformat
import bson
from pylons import c
from ming.base import Object
from allura import model as M
from allura.command.show_models import dfs, build_model_inheritance_graph
log = logging.getLogger('update-acls')
options = None
optparser = OptionParser(usage='allurapaste script <ini file> -- %prog [options] [neighborhood1...]')
optparser.add_option('-t', '--test', dest='test', action='store_true')
main_db = M.main_doc_session.db
c_neighborhood = main_db.neighborhood
c_project = main_db.project
c_user = main_db.user
c_project_role = main_db.project_role
c.project = Object(
database_uri=c_project.find().next()['database_uri'])
project_db = M.project_doc_session.db
c_app_config = project_db.config
def main():
global options
options, neighborhoods = optparser.parse_args()
if neighborhoods:
log.info('Updating neighborhoods: %s', neighborhoods)
q_neighborhoods = list(c_neighborhood.find(dict(name={'$in': neighborhoods })))
neighborhood_ids=[ n['_id'] for n in q_neighborhoods ]
q_projects = list(c_project.find(dict(neighborhood_id={'$in': neighborhood_ids})))
project_ids = list(p['_id'] for p in q_projects)
q_app_config = list(c_app_config.find(dict(project_id={'$in': project_ids})))
log.info('... %d neighborhoods', len(q_neighborhoods))
log.info('... %d projects', len(q_projects))
log.info('... %d app configs', len(q_app_config))
else:
q_neighborhoods = c_neighborhood.find()
q_projects = c_project.find()
q_app_config = c_app_config.find()
log.info('Updating all neighborhoods')
# Update project acls
log.info('====================================')
log.info('Update project ACLs')
for p in q_projects:
update_project_acl(p)
if not options.test: c_project.save(p)
# Update neighborhood acls
log.info('====================================')
log.info('Update neighborhood ACLs')
for n in q_neighborhoods:
p = c_project.find(dict(
neighborhood_id=n['_id'], shortname='--init--')).next()
update_neighborhood_acl(n,p)
if not options.test:
c_neighborhood.save(n)
c_project.save(p)
graph = build_model_inheritance_graph()
# Update app config acls
log.info('====================================')
log.info('Update appconfig ACLs')
for ac in q_app_config:
simple_acl_update(ac, 'app_config')
if not options.test: c_app_config.save(ac)
# Update artifact acls
log.info('====================================')
log.info('Update artifact ACLs for %s', ac['_id'])
for _, a_cls in dfs(M.Artifact, graph):
c_artifact = project_db[a_cls.__mongometa__.name]
for a in c_artifact.find(dict(app_config_id=ac['_id'])):
empty_acl = a['acl'] == []
simple_acl_update(a, a_cls.__mongometa__.name)
if not options.test and not empty_acl: c_artifact.save(a)
def update_project_acl(project_doc):
'''Convert the old dict-style ACL to a list of ALLOW ACEs. Also move the
security,tool,delete perms to 'admin'
'''
if not isinstance(project_doc['acl'], dict):
log.warning('Project %s is already updated', project_doc['shortname'])
return
perm_map = dict(
read='read',
create='create',
update='update',
security='admin',
tool='admin',
delete='admin')
new_acl = []
for perm, role_ids in sorted(project_doc['acl'].iteritems()):
perm = perm_map[perm]
for rid in role_ids:
if c_project_role.find(dict(_id=rid)).count() == 0: continue
_grant(new_acl, perm, rid)
if options.test:
log.info('--- update %s\n%s\n%s\n---',
project_doc['shortname'],
pformat(_format_acd(project_doc['acl'])),
pformat(map(_format_ace, new_acl)))
project_doc['acl'] = new_acl
def update_neighborhood_acl(neighborhood_doc, init_doc):
'''Convert nbhd admins users to --init-- project admins'''
if options.test: log.info('Update nbhd %s', neighborhood_doc['name'])
if 'acl' not in neighborhood_doc:
log.warning('Neighborhood %s already updated', neighborhood_doc['name'])
return
p = Object(init_doc)
p.root_project=p
r_anon = _project_role(init_doc['_id'], '*anonymous')
r_auth = _project_role(init_doc['_id'], '*authenticated')
r_admin = _project_role(init_doc['_id'], 'Admin')
acl = neighborhood_doc['acl']
new_acl = list(init_doc['acl'])
assert acl['read'] == [None] # nbhd should be public
for uid in acl['admin'] + acl['moderate']:
u = c_user.find(dict(_id=uid)).next()
if options.test:
log.info('... grant nbhd admin to: %s', u['username'])
continue
role = _project_role(init_doc['_id'], user_id=uid)
if r_admin['_id'] not in role['roles']:
role['roles'].append(r_admin['_id'])
c_project_role.save(role)
_grant(new_acl, 'read', r_anon['_id'])
_grant(new_acl, 'admin', r_admin['_id'])
_grant(new_acl, 'register', r_admin['_id'])
if acl['create'] == [ ]:
if options.test: log.info('grant register to auth')
_grant(new_acl, 'register', r_auth['_id'])
del neighborhood_doc['acl']
if options.test:
log.info('--- new init acl:\n%s\n%s\n---',
pformat(_format_acd(init_doc['acl'])),
pformat(map(_format_ace, new_acl)))
init_doc['acl'] = new_acl
def _project_role(project_id, name=None, user_id=None):
doc = dict(project_id=project_id)
if name:
doc['name'] = name
else:
doc['user_id'] = user_id
for role in c_project_role.find(doc):
return role
assert name is None
doc.update(
_id=bson.ObjectId(),
roles=[])
c_project_role.save(doc)
return doc
def simple_acl_update(doc, collection_name):
'''Update dict-style to list-style ACL'''
if not isinstance(doc['acl'], dict):
log.warning('Already upgraded %s: %s', collection_name, doc)
return
new_acl = []
for perm, role_ids in sorted(doc['acl'].iteritems()):
for rid in role_ids:
_grant(new_acl, perm, rid)
if options.test and doc['acl']:
log.info('--- update %s %s\n%s\n%s\n---',
collection_name, doc['_id'],
pformat(_format_acd(doc['acl'])),
pformat(map(_format_ace, new_acl)))
doc['acl'] = new_acl
def _grant(acl, permission, role_id):
ace = dict(
access='ALLOW',
permission=permission,
role_id=role_id)
if ace not in acl:
acl.append(ace)
def _format_ace(ace):
if isinstance(ace, basestring): return ace
return '(%s, %s, %s)' % (
ace['access'], ace['permission'], _format_role(ace['role_id']))
def _format_role(rid):
for role in c_project_role.find(dict(_id=rid)):
if role['name']:
return role['name']
if role['user_id']:
u = c_user.find(_id=role['user_id']).next()
return u['username']
break
return '--invalid--'
def _format_acd(acd):
return dict(
(k, map(_format_role, v))
for k,v in acd.iteritems())
if __name__ == '__main__':
main()