|
a/scripts/update-acls.py |
|
b/scripts/update-acls.py |
1 |
import logging
|
1 |
import logging
|
2 |
from optparse import OptionParser
|
2 |
from optparse import OptionParser
|
3 |
from pprint import pformat
|
3 |
from pprint import pformat
|
4 |
|
4 |
|
|
|
5 |
import bson
|
5 |
from pylons import c
|
6 |
from pylons import c
|
6 |
from ming.base import Object
|
7 |
from ming.base import Object
|
7 |
from ming.orm import ThreadLocalORMSession
|
|
|
8 |
|
8 |
|
9 |
from allura import model as M
|
9 |
from allura import model as M
|
10 |
from allura.command.show_models import dfs, build_model_inheritance_graph
|
10 |
from allura.command.show_models import dfs, build_model_inheritance_graph
|
11 |
|
11 |
|
12 |
log = logging.getLogger('update-acls')
|
12 |
log = logging.getLogger('update-acls')
|
13 |
|
13 |
|
14 |
options = None
|
14 |
options = None
|
15 |
optparser = OptionParser(usage='allurapaste script <ini file> -- %prog [options] [neighborhood1...]')
|
15 |
optparser = OptionParser(usage='allurapaste script <ini file> -- %prog [options] [neighborhood1...]')
|
16 |
optparser.add_option('-t', '--test', dest='test', action='store_true')
|
16 |
optparser.add_option('-t', '--test', dest='test', action='store_true')
|
17 |
|
17 |
|
|
|
18 |
main_db = M.main_doc_session.db
|
|
|
19 |
c_neighborhood = main_db.neighborhood
|
|
|
20 |
c_project = main_db.project
|
|
|
21 |
c_user = main_db.user
|
|
|
22 |
c_project_role = main_db.project_role
|
|
|
23 |
c.project = Object(
|
|
|
24 |
database_uri=c_project.find().next()['database_uri'])
|
|
|
25 |
|
|
|
26 |
project_db = M.project_doc_session.db
|
|
|
27 |
c_app_config = project_db.config
|
|
|
28 |
|
18 |
def main():
|
29 |
def main():
|
19 |
global options
|
30 |
global options
|
20 |
options, neighborhoods = optparser.parse_args()
|
31 |
options, neighborhoods = optparser.parse_args()
|
21 |
neighborhood = M.main_doc_session.db.neighborhood
|
|
|
22 |
project = M.main_doc_session.db.project
|
|
|
23 |
c.project = Object(
|
|
|
24 |
database_uri=project.find().next()['database_uri'])
|
|
|
25 |
app_config = M.project_doc_session.db.config
|
|
|
26 |
if neighborhoods:
|
32 |
if neighborhoods:
|
27 |
log.info('Updating neighborhoods: %s', neighborhoods)
|
33 |
log.info('Updating neighborhoods: %s', neighborhoods)
|
28 |
q_neighborhoods = list(neighborhood.find(dict(name={'$in': neighborhoods })))
|
34 |
q_neighborhoods = list(c_neighborhood.find(dict(name={'$in': neighborhoods })))
|
29 |
neighborhood_ids=[ n['_id'] for n in q_neighborhoods ]
|
35 |
neighborhood_ids=[ n['_id'] for n in q_neighborhoods ]
|
30 |
q_projects = list(project.find(dict(neighborhood_id={'$in': neighborhood_ids})))
|
36 |
q_projects = list(c_project.find(dict(neighborhood_id={'$in': neighborhood_ids})))
|
31 |
project_ids = list(p['_id'] for p in q_projects)
|
37 |
project_ids = list(p['_id'] for p in q_projects)
|
32 |
q_app_config = list(app_config.find(dict(project_id={'$in': project_ids})))
|
38 |
q_app_config = list(c_app_config.find(dict(project_id={'$in': project_ids})))
|
33 |
log.info('... %d neighborhoods', len(q_neighborhoods))
|
39 |
log.info('... %d neighborhoods', len(q_neighborhoods))
|
34 |
log.info('... %d projects', len(q_projects))
|
40 |
log.info('... %d projects', len(q_projects))
|
35 |
log.info('... %d app configs', len(q_app_config))
|
41 |
log.info('... %d app configs', len(q_app_config))
|
36 |
else:
|
42 |
else:
|
37 |
q_neighborhoods = neighborhood.find()
|
43 |
q_neighborhoods = c_neighborhood.find()
|
38 |
q_projects = project.find()
|
44 |
q_projects = c_project.find()
|
39 |
q_app_config = app_config.find()
|
45 |
q_app_config = c_app_config.find()
|
40 |
log.info('Updating all neighborhoods')
|
46 |
log.info('Updating all neighborhoods')
|
41 |
# Update project acls
|
47 |
# Update project acls
|
42 |
log.info('====================================')
|
48 |
log.info('====================================')
|
43 |
log.info('Update project ACLs')
|
49 |
log.info('Update project ACLs')
|
44 |
for p in q_projects:
|
50 |
for p in q_projects:
|
45 |
update_project_acl(p)
|
51 |
update_project_acl(p)
|
46 |
if not options.test: project.save(p)
|
52 |
if not options.test: c_project.save(p)
|
47 |
# Update neighborhood acls
|
53 |
# Update neighborhood acls
|
48 |
log.info('====================================')
|
54 |
log.info('====================================')
|
49 |
log.info('Update neighborhood ACLs')
|
55 |
log.info('Update neighborhood ACLs')
|
50 |
for n in q_neighborhoods:
|
56 |
for n in q_neighborhoods:
|
51 |
p = project.find(dict(
|
57 |
p = c_project.find(dict(
|
52 |
neighborhood_id=n['_id'], shortname='--init--')).next()
|
58 |
neighborhood_id=n['_id'], shortname='--init--')).next()
|
53 |
update_neighborhood_acl(n, p)
|
59 |
update_neighborhood_acl(n,p)
|
54 |
if not options.test:
|
60 |
if not options.test:
|
55 |
neighborhood.save(n)
|
61 |
c_neighborhood.save(n)
|
56 |
project.save(p)
|
62 |
c_project.save(p)
|
57 |
ThreadLocalORMSession.flush_all()
|
|
|
58 |
ThreadLocalORMSession.close_all()
|
|
|
59 |
graph = build_model_inheritance_graph()
|
63 |
graph = build_model_inheritance_graph()
|
60 |
# Update app config acls
|
64 |
# Update app config acls
|
61 |
log.info('====================================')
|
65 |
log.info('====================================')
|
62 |
log.info('Update appconfig ACLs')
|
66 |
log.info('Update appconfig ACLs')
|
63 |
for ac in q_app_config:
|
67 |
for ac in q_app_config:
|
64 |
simple_acl_update(ac)
|
68 |
simple_acl_update(ac, 'app_config')
|
65 |
if not options.test: app_config.save(ac)
|
69 |
if not options.test: c_app_config.save(ac)
|
66 |
# Update artifact acls
|
70 |
# Update artifact acls
|
67 |
log.info('====================================')
|
71 |
log.info('====================================')
|
68 |
log.info('Update artifact ACLs')
|
72 |
log.info('Update artifact ACLs for %s', ac['_id'])
|
69 |
for _, a_cls in dfs(M.Artifact, graph):
|
73 |
for _, a_cls in dfs(M.Artifact, graph):
|
70 |
artifact = M.project_doc_session.db[
|
74 |
c_artifact = project_db[a_cls.__mongometa__.name]
|
71 |
a_cls.__mongometa__.name]
|
|
|
72 |
for a in artifact.find(dict(app_config_id=ac['_id'])):
|
75 |
for a in c_artifact.find(dict(app_config_id=ac['_id'])):
|
73 |
empty_acl = not a['acl']
|
76 |
empty_acl = not a['acl']
|
74 |
simple_acl_update(a)
|
77 |
simple_acl_update(a, a_cls.__mongometa__.name)
|
75 |
if not options.test and not empty_acl: artifact.save(a)
|
78 |
if not options.test and not empty_acl: c_artifact.save(a)
|
76 |
|
79 |
|
77 |
def update_project_acl(project_doc):
|
80 |
def update_project_acl(project_doc):
|
78 |
'''Convert the old dict-style ACL to a list of ALLOW ACEs. Also move the
|
81 |
'''Convert the old dict-style ACL to a list of ALLOW ACEs. Also move the
|
79 |
security,tool,delete perms to 'admin'
|
82 |
security,tool,delete perms to 'admin'
|
80 |
'''
|
83 |
'''
|
81 |
project_role = M.project_doc_session.db.project_role
|
|
|
82 |
if not isinstance(project_doc['acl'], dict):
|
84 |
if not isinstance(project_doc['acl'], dict):
|
83 |
log.warning('Project %s is already updated', project_doc['shortname'])
|
85 |
log.warning('Project %s is already updated', project_doc['shortname'])
|
84 |
return
|
86 |
return
|
85 |
perm_map = dict(
|
87 |
perm_map = dict(
|
86 |
read='read',
|
88 |
read='read',
|
|
... |
|
... |
91 |
delete='admin')
|
93 |
delete='admin')
|
92 |
new_acl = []
|
94 |
new_acl = []
|
93 |
for perm, role_ids in sorted(project_doc['acl'].iteritems()):
|
95 |
for perm, role_ids in sorted(project_doc['acl'].iteritems()):
|
94 |
perm = perm_map[perm]
|
96 |
perm = perm_map[perm]
|
95 |
for rid in role_ids:
|
97 |
for rid in role_ids:
|
96 |
if project_role.find(dict(_id=rid)).count() == 0: continue
|
98 |
if c_project_role.find(dict(_id=rid)).count() == 0: continue
|
97 |
_grant(new_acl, perm, rid)
|
99 |
_grant(new_acl, perm, rid)
|
98 |
if options.test:
|
100 |
if options.test:
|
99 |
log.info('--- update %s\n%s\n%s\n---',
|
101 |
log.info('--- update %s\n%s\n%s\n---',
|
100 |
project_doc['shortname'],
|
102 |
project_doc['shortname'],
|
101 |
pformat(_format_acd(project_doc['acl'])),
|
103 |
pformat(_format_acd(project_doc['acl'])),
|
|
... |
|
... |
105 |
def update_neighborhood_acl(neighborhood_doc, init_doc):
|
107 |
def update_neighborhood_acl(neighborhood_doc, init_doc):
|
106 |
'''Convert nbhd admins users to --init-- project admins'''
|
108 |
'''Convert nbhd admins users to --init-- project admins'''
|
107 |
if options.test: log.info('Update nbhd %s', neighborhood_doc['name'])
|
109 |
if options.test: log.info('Update nbhd %s', neighborhood_doc['name'])
|
108 |
if 'acl' not in neighborhood_doc:
|
110 |
if 'acl' not in neighborhood_doc:
|
109 |
log.warning('Neighborhood %s already updated', neighborhood_doc['name'])
|
111 |
log.warning('Neighborhood %s already updated', neighborhood_doc['name'])
|
|
|
112 |
return
|
110 |
p = Object(init_doc)
|
113 |
p = Object(init_doc)
|
111 |
p.root_project=p
|
114 |
p.root_project=p
|
112 |
r_auth = M.ProjectRole.authenticated(p)._id
|
115 |
r_anon = _project_role(init_doc['_id'], '*anonymous')
|
113 |
r_admin = M.ProjectRole.by_name('Admin', p)._id
|
116 |
r_auth = _project_role(init_doc['_id'], '*authenticated')
|
|
|
117 |
r_admin = _project_role(init_doc['_id'], 'Admin')
|
114 |
acl = neighborhood_doc['acl']
|
118 |
acl = neighborhood_doc['acl']
|
115 |
new_acl = list(init_doc['acl'])
|
119 |
new_acl = list(init_doc['acl'])
|
116 |
assert acl['read'] == [None] # nbhd should be public
|
120 |
assert acl['read'] == [None] # nbhd should be public
|
117 |
for uid in acl['admin'] + acl['moderate']:
|
121 |
for uid in acl['admin'] + acl['moderate']:
|
118 |
u = M.User.query.get(_id=uid)
|
122 |
u = c_user.find(dict(_id=uid)).next()
|
|
|
123 |
if options.test:
|
119 |
if options.test: log.info('... grant nbhd admin to: %s', u.username)
|
124 |
log.info('... grant nbhd admin to: %s', u['username'])
|
120 |
role = M.ProjectRole.upsert(user_id=uid, project_id=init_doc['_id'])
|
125 |
continue
|
|
|
126 |
role = _project_role(init_doc['_id'], user_id=uid)
|
121 |
if r_admin not in role.roles:
|
127 |
if r_admin['_id'] not in role['roles']:
|
122 |
role.roles.append(r_admin)
|
128 |
role['roles'].append(r_admin['_id'])
|
|
|
129 |
c_project_role.save(role)
|
|
|
130 |
_grant(new_acl, 'read', r_anon['_id'])
|
|
|
131 |
_grant(new_acl, 'admin', r_admin['_id'])
|
123 |
_grant(new_acl, 'register', r_admin)
|
132 |
_grant(new_acl, 'register', r_admin['_id'])
|
124 |
if acl['create'] == [ ]:
|
133 |
if acl['create'] == [ ]:
|
125 |
if options.test: log.info('grant register to auth')
|
134 |
if options.test: log.info('grant register to auth')
|
126 |
_grant(new_acl, 'register', r_auth)
|
135 |
_grant(new_acl, 'register', r_auth['_id'])
|
127 |
del neighborhood_doc['acl']
|
136 |
del neighborhood_doc['acl']
|
128 |
if options.test:
|
137 |
if options.test:
|
129 |
log.info('--- new init acl:\n%s\n%s\n---',
|
138 |
log.info('--- new init acl:\n%s\n%s\n---',
|
130 |
pformat(_format_acd(init_doc['acl'])),
|
139 |
pformat(_format_acd(init_doc['acl'])),
|
131 |
pformat(map(_format_ace, new_acl)))
|
140 |
pformat(map(_format_ace, new_acl)))
|
132 |
init_doc['acl'] = new_acl
|
141 |
init_doc['acl'] = new_acl
|
133 |
|
142 |
|
|
|
143 |
def _project_role(project_id, name=None, user_id=None):
|
|
|
144 |
doc = dict(project_id=project_id)
|
|
|
145 |
if name:
|
|
|
146 |
doc['name'] = name
|
|
|
147 |
else:
|
|
|
148 |
doc['user_id'] = user_id
|
|
|
149 |
for role in c_project_role.find(doc):
|
|
|
150 |
return role
|
|
|
151 |
assert name is None
|
|
|
152 |
doc.update(
|
|
|
153 |
_id=bson.ObjectId(),
|
|
|
154 |
roles=[])
|
|
|
155 |
c_project_role.save(doc)
|
|
|
156 |
return doc
|
|
|
157 |
|
|
|
158 |
|
134 |
def simple_acl_update(doc):
|
159 |
def simple_acl_update(doc, collection_name):
|
135 |
'''Update dict-style to list-style ACL'''
|
160 |
'''Update dict-style to list-style ACL'''
|
136 |
if not isinstance(doc['acl'], dict):
|
161 |
if not isinstance(doc['acl'], dict):
|
137 |
log.warning('Already upgraded %s' % doc)
|
162 |
log.warning('Already upgraded %s: %s', collection_name, doc)
|
138 |
return
|
163 |
return
|
139 |
|
164 |
|
140 |
new_acl = []
|
165 |
new_acl = []
|
141 |
for perm, role_ids in sorted(doc['acl'].iteritems()):
|
166 |
for perm, role_ids in sorted(doc['acl'].iteritems()):
|
142 |
for rid in role_ids:
|
167 |
for rid in role_ids:
|
143 |
_grant(new_acl, perm, rid)
|
168 |
_grant(new_acl, perm, rid)
|
144 |
if options.test and doc['acl']:
|
169 |
if options.test and doc['acl']:
|
145 |
log.info('--- update\n%s\n%s\n---',
|
170 |
log.info('--- update %s %s\n%s\n%s\n---',
|
|
|
171 |
collection_name, doc['_id'],
|
146 |
pformat(_format_acd(doc['acl'])),
|
172 |
pformat(_format_acd(doc['acl'])),
|
147 |
pformat(map(_format_ace, new_acl)))
|
173 |
pformat(map(_format_ace, new_acl)))
|
148 |
doc['acl'] = new_acl
|
174 |
doc['acl'] = new_acl
|
149 |
|
175 |
|
150 |
def _grant(acl, permission, role_id):
|
176 |
def _grant(acl, permission, role_id):
|
|
... |
|
... |
159 |
if isinstance(ace, basestring): return ace
|
185 |
if isinstance(ace, basestring): return ace
|
160 |
return '(%s, %s, %s)' % (
|
186 |
return '(%s, %s, %s)' % (
|
161 |
ace['access'], ace['permission'], _format_role(ace['role_id']))
|
187 |
ace['access'], ace['permission'], _format_role(ace['role_id']))
|
162 |
|
188 |
|
163 |
def _format_role(rid):
|
189 |
def _format_role(rid):
|
164 |
role = M.ProjectRole.query.get(_id=rid)
|
190 |
for role in c_project_role.find(dict(_id=rid)):
|
165 |
if role:
|
|
|
166 |
if role.name:
|
191 |
if role['name']:
|
167 |
return role.name
|
192 |
return role['name']
|
168 |
if role.user:
|
193 |
if role['user_id']:
|
|
|
194 |
u = c_user.find(_id=role['user_id']).next()
|
169 |
return role.user.username
|
195 |
return u['username']
|
|
|
196 |
break
|
170 |
return '--invalid--'
|
197 |
return '--invalid--'
|
171 |
|
198 |
|
172 |
def _format_acd(acd):
|
199 |
def _format_acd(acd):
|
173 |
return dict(
|
200 |
return dict(
|
174 |
(k, map(_format_role, v))
|
201 |
(k, map(_format_role, v))
|