Merge lp:~soren/ubuntu/oneiric/nova/reconcile-with-security into lp:ubuntu/oneiric-proposed/nova
- Oneiric (11.10)
- reconcile-with-security
- Merge into oneiric-proposed
Status: | Rejected |
---|---|
Rejected by: | Soren Hansen |
Proposed branch: | lp:~soren/ubuntu/oneiric/nova/reconcile-with-security |
Merge into: | lp:ubuntu/oneiric-proposed/nova |
Diff against target: |
1610 lines (+1514/-9) 10 files modified
.pc/applied-patches (+1/-0) .pc/security-fix-lp868360.patch/Authors (+125/-0) .pc/security-fix-lp868360.patch/nova/api/ec2/__init__.py (+440/-0) .pc/security-fix-lp868360.patch/nova/auth/manager.py (+842/-0) Authors (+1/-0) debian/changelog (+30/-0) debian/patches/security-fix-lp868360.patch (+70/-0) debian/patches/series (+1/-0) nova/api/ec2/__init__.py (+2/-1) nova/auth/manager.py (+2/-8) |
To merge this branch: | bzr merge lp:~soren/ubuntu/oneiric/nova/reconcile-with-security |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Development Team | Pending | ||
Review via email: mp+80562@code.launchpad.net |
Commit message
Description of the change
Merged oneiric-security into oneiric-proposed, added changelog entry.
Dave Walker (davewalker) wrote : | # |
Soren Hansen (soren) wrote : | # |
We were working on a package update for -proposed. I forget if it ever landed, but I don't think it did.
However, as this work was progressing, a security update landed in -security. We obviously want to fold that security updates into our -proposed branch. That's what this branch does.
Unmerged revisions
- 47. By Soren Hansen
-
Merge update from oneiric-security and add new changelog entry for -proposed:
* 2011.3-0ubuntu6.1 never made it past -proposed, due to 2011.3-
0ubuntu6.2 which was a security fix (without the changes from
2011.3-0ubuntu6. 1). This upload brings the changes from 2011.3-
0ubuntu6.1 back.
* SECURITY UPDATE: fix information leak via invalid key
debina/patches/ security- fix-lp868360. patch: adjust nova/auth/ manager. py
to not return access, secret or admin fields for User error and
project_manager_ id, description and member_ids for Project
- LP: #868360
- CVE-2011-XXXX
Preview Diff
1 | === modified file '.pc/applied-patches' |
2 | --- .pc/applied-patches 2011-10-12 14:33:25 +0000 |
3 | +++ .pc/applied-patches 2011-10-27 13:05:25 +0000 |
4 | @@ -11,3 +11,4 @@ |
5 | fix-lp863305-images-permission.patch |
6 | fix-lp838581-removed-db_pool-complexities.patch |
7 | fix-iscsi-target-path.patch |
8 | +security-fix-lp868360.patch |
9 | |
10 | === added directory '.pc/security-fix-lp868360.patch' |
11 | === added file '.pc/security-fix-lp868360.patch/Authors' |
12 | --- .pc/security-fix-lp868360.patch/Authors 1970-01-01 00:00:00 +0000 |
13 | +++ .pc/security-fix-lp868360.patch/Authors 2011-10-27 13:05:25 +0000 |
14 | @@ -0,0 +1,125 @@ |
15 | +Adam Gandelman <adamg@canonical.com> |
16 | +Adam Johnson <adjohn@gmail.com> |
17 | +Alex Meade <alex.meade@rackspace.com> |
18 | +Alexander Sakhnov <asakhnov@mirantis.com> |
19 | +Andrey Brindeyev <abrindeyev@griddynamics.com> |
20 | +Andy Smith <code@term.ie> |
21 | +Andy Southgate <andy.southgate@citrix.com> |
22 | +Anne Gentle <anne@openstack.org> |
23 | +Anthony Young <sleepsonthefloor@gmail.com> |
24 | +Antony Messerli <ant@openstack.org> |
25 | +Armando Migliaccio <Armando.Migliaccio@eu.citrix.com> |
26 | +Arvind Somya <asomya@cisco.com> |
27 | +Bilal Akhtar <bilalakhtar@ubuntu.com> |
28 | +Brad Hall <brad@nicira.com> |
29 | +Brad McConnell <bmcconne@rackspace.com> |
30 | +Brian Lamar <brian.lamar@rackspace.com> |
31 | +Brian Schott <bschott@isi.edu> |
32 | +Brian Waldon <brian.waldon@rackspace.com> |
33 | +Chiradeep Vittal <chiradeep@cloud.com> |
34 | +Chmouel Boudjnah <chmouel@chmouel.com> |
35 | +Chris Behrens <cbehrens@codestud.com> |
36 | +Christian Berendt <berendt@b1-systems.de> |
37 | +Christopher MacGown <chris@pistoncloud.com> |
38 | +Chuck Short <zulcss@ubuntu.com> |
39 | +Cory Wright <corywright@gmail.com> |
40 | +Dan Prince <dan.prince@rackspace.com> |
41 | +Dan Wendlandt <dan@nicira.com> |
42 | +Dave Walker <DaveWalker@ubuntu.com> |
43 | +David Pravec <David.Pravec@danix.org> |
44 | +David Subiros <david.perez5@hp.com> |
45 | +Dean Troyer <dtroyer@gmail.com> |
46 | +Devendra Modium <dmodium@isi.edu> |
47 | +Devin Carlen <devin.carlen@gmail.com> |
48 | +Donal Lafferty <donal.lafferty@citrix.com> |
49 | +Ed Leafe <ed@leafe.com> |
50 | +Edouard Thuleau <thuleau@gmail.com> |
51 | +Eldar Nugaev <reldan@oscloud.ru> |
52 | +Eric Day <eday@oddments.org> |
53 | +Eric Windisch <eric@cloudscaling.com> |
54 | +Ewan Mellor <ewan.mellor@citrix.com> |
55 | +Gabe Westmaas <gabe.westmaas@rackspace.com> |
56 | +Hisaharu Ishii <ishii.hisaharu@lab.ntt.co.jp> |
57 | +Hisaki Ohara <hisaki.ohara@intel.com> |
58 | +Ilya Alekseyev <ilyaalekseyev@acm.org> |
59 | +Isaku Yamahata <yamahata@valinux.co.jp> |
60 | +Jake Dahn <jake@ansolabs.com> |
61 | +Jason Cannavale <jason.cannavale@rackspace.com> |
62 | +Jason Koelker <jason@koelker.net> |
63 | +Jay Pipes <jaypipes@gmail.com> |
64 | +Jesse Andrews <anotherjesse@gmail.com> |
65 | +Jimmy Bergman <jimmy@sigint.se> |
66 | +Joe Heck <heckj@mac.com> |
67 | +Joel Moore <joelbm24@gmail.com> |
68 | +Johannes Erdfelt <johannes.erdfelt@rackspace.com> |
69 | +John Dewey <john@dewey.ws> |
70 | +John Tran <jtran@attinteractive.com> |
71 | +Jonathan Bryce <jbryce@jbryce.com> |
72 | +Jordan Rinke <jordan@openstack.org> |
73 | +Joseph Suh <jsuh@isi.edu> |
74 | +Josh Durgin <joshd@hq.newdream.net> |
75 | +Josh Kearney <josh@jk0.org> |
76 | +Josh Kleinpeter <josh@kleinpeter.org> |
77 | +Joshua McKenty <jmckenty@gmail.com> |
78 | +Justin Santa Barbara <justin@fathomdb.com> |
79 | +Justin Shepherd <jshepher@rackspace.com> |
80 | +Kei Masumoto <masumotok@nttdata.co.jp> |
81 | +Keisuke Tagami <tagami.keisuke@lab.ntt.co.jp> |
82 | +masumoto<masumotok@nttdata.co.jp> |
83 | +Ken Pepple <ken.pepple@gmail.com> |
84 | +Kevin Bringard <kbringard@attinteractive.com> |
85 | +Kevin L. Mitchell <kevin.mitchell@rackspace.com> |
86 | +Kirill Shileev <kshileev@gmail.com> |
87 | +Koji Iida <iida.koji@lab.ntt.co.jp> |
88 | +Loganathan Parthipan <parthipan@hp.com> |
89 | +Lorin Hochstein <lorin@isi.edu> |
90 | +Lvov Maxim <usrleon@gmail.com> |
91 | +Mandell Degerness <mdegerne@gmail.com> |
92 | +Mark McLoughlin <markmc@redhat.com> |
93 | +Mark Washenberger <mark.washenberger@rackspace.com> |
94 | +Masanori Itoh <itoumsn@nttdata.co.jp> |
95 | +Matt Dietz <matt.dietz@rackspace.com> |
96 | +Matthew Hooker <matt@cloudscaling.com> |
97 | +Michael Gundlach <michael.gundlach@rackspace.com> |
98 | +Mike Scherbakov <mihgen@gmail.com> |
99 | +Mohammed Naser <mnaser@vexxhost.com> |
100 | +Monsyne Dragon <mdragon@rackspace.com> |
101 | +Monty Taylor <mordred@inaugust.com> |
102 | +MORITA Kazutaka <morita.kazutaka@gmail.com> |
103 | +Muneyuki Noguchi <noguchimn@nttdata.co.jp> |
104 | +Nachi Ueno <ueno.nachi@lab.ntt.co.jp> |
105 | +Naveed Massjouni <naveedm9@gmail.com> |
106 | +Nikolay Sokolov <nsokolov@griddynamics.com> |
107 | +Nirmal Ranganathan <nirmal.ranganathan@rackspace.com> |
108 | +Paul Voccio <paul@openstack.org> |
109 | +Renuka Apte <renuka.apte@citrix.com> |
110 | +Ricardo Carrillo Cruz <emaildericky@gmail.com> |
111 | +Rick Clark <rick@openstack.org> |
112 | +Rick Harris <rconradharris@gmail.com> |
113 | +Rob Kost <kost@isi.edu> |
114 | +Robie Basak <robie.basak@canonical.com> |
115 | +Ryan Lane <rlane@wikimedia.org> |
116 | +Ryan Lucio <rlucio@internap.com> |
117 | +Ryu Ishimoto <ryu@midokura.jp> |
118 | +Salvatore Orlando <salvatore.orlando@eu.citrix.com> |
119 | +Sandy Walsh <sandy.walsh@rackspace.com> |
120 | +Sateesh Chodapuneedi <sateesh.chodapuneedi@citrix.com> |
121 | +Scott Moser <smoser@ubuntu.com> |
122 | +Soren Hansen <soren.hansen@rackspace.com> |
123 | +Stephanie Reese <reese.sm@gmail.com> |
124 | +Thierry Carrez <thierry@openstack.org> |
125 | +Tim Simpson <tim.simpson@rackspace.com> |
126 | +Todd Willey <todd@ansolabs.com> |
127 | +Trey Morris <trey.morris@rackspace.com> |
128 | +Troy Toman <troy.toman@rackspace.com> |
129 | +Tushar Patil <tushar.vitthal.patil@gmail.com> |
130 | +Vasiliy Shlykov <vash@vasiliyshlykov.org> |
131 | +Vishvananda Ishaya <vishvananda@gmail.com> |
132 | +Vivek Y S <vivek.ys@gmail.com> |
133 | +Vladimir Popovski <vladimir@zadarastorage.com> |
134 | +William Wolf <throughnothing@gmail.com> |
135 | +Yoshiaki Tamura <yoshi@midokura.jp> |
136 | +Youcef Laribi <Youcef.Laribi@eu.citrix.com> |
137 | +Yuriy Taraday <yorik.sar@gmail.com> |
138 | +Zhixue Wu <Zhixue.Wu@citrix.com> |
139 | +Zed Shaw <zedshaw@zedshaw.com> |
140 | |
141 | === added directory '.pc/security-fix-lp868360.patch/nova' |
142 | === added directory '.pc/security-fix-lp868360.patch/nova/api' |
143 | === added directory '.pc/security-fix-lp868360.patch/nova/api/ec2' |
144 | === added file '.pc/security-fix-lp868360.patch/nova/api/ec2/__init__.py' |
145 | --- .pc/security-fix-lp868360.patch/nova/api/ec2/__init__.py 1970-01-01 00:00:00 +0000 |
146 | +++ .pc/security-fix-lp868360.patch/nova/api/ec2/__init__.py 2011-10-27 13:05:25 +0000 |
147 | @@ -0,0 +1,440 @@ |
148 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
149 | + |
150 | +# Copyright 2010 United States Government as represented by the |
151 | +# Administrator of the National Aeronautics and Space Administration. |
152 | +# All Rights Reserved. |
153 | +# |
154 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
155 | +# not use this file except in compliance with the License. You may obtain |
156 | +# a copy of the License at |
157 | +# |
158 | +# http://www.apache.org/licenses/LICENSE-2.0 |
159 | +# |
160 | +# Unless required by applicable law or agreed to in writing, software |
161 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
162 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
163 | +# License for the specific language governing permissions and limitations |
164 | +# under the License. |
165 | +""" |
166 | +Starting point for routing EC2 requests. |
167 | + |
168 | +""" |
169 | + |
170 | +from urlparse import urlparse |
171 | + |
172 | +import eventlet |
173 | +from eventlet.green import httplib |
174 | +import webob |
175 | +import webob.dec |
176 | +import webob.exc |
177 | + |
178 | +from nova import context |
179 | +from nova import exception |
180 | +from nova import flags |
181 | +from nova import log as logging |
182 | +from nova import utils |
183 | +from nova import wsgi |
184 | +from nova.api.ec2 import apirequest |
185 | +from nova.api.ec2 import ec2utils |
186 | +from nova.auth import manager |
187 | + |
188 | +FLAGS = flags.FLAGS |
189 | +LOG = logging.getLogger("nova.api") |
190 | +flags.DEFINE_integer('lockout_attempts', 5, |
191 | + 'Number of failed auths before lockout.') |
192 | +flags.DEFINE_integer('lockout_minutes', 15, |
193 | + 'Number of minutes to lockout if triggered.') |
194 | +flags.DEFINE_integer('lockout_window', 15, |
195 | + 'Number of minutes for lockout window.') |
196 | +flags.DECLARE('use_forwarded_for', 'nova.api.auth') |
197 | + |
198 | + |
199 | +class RequestLogging(wsgi.Middleware): |
200 | + """Access-Log akin logging for all EC2 API requests.""" |
201 | + |
202 | + @webob.dec.wsgify(RequestClass=wsgi.Request) |
203 | + def __call__(self, req): |
204 | + start = utils.utcnow() |
205 | + rv = req.get_response(self.application) |
206 | + self.log_request_completion(rv, req, start) |
207 | + return rv |
208 | + |
209 | + def log_request_completion(self, response, request, start): |
210 | + apireq = request.environ.get('ec2.request', None) |
211 | + if apireq: |
212 | + controller = apireq.controller |
213 | + action = apireq.action |
214 | + else: |
215 | + controller = None |
216 | + action = None |
217 | + ctxt = request.environ.get('nova.context', None) |
218 | + delta = utils.utcnow() - start |
219 | + seconds = delta.seconds |
220 | + microseconds = delta.microseconds |
221 | + LOG.info( |
222 | + "%s.%ss %s %s %s %s:%s %s [%s] %s %s", |
223 | + seconds, |
224 | + microseconds, |
225 | + request.remote_addr, |
226 | + request.method, |
227 | + "%s%s" % (request.script_name, request.path_info), |
228 | + controller, |
229 | + action, |
230 | + response.status_int, |
231 | + request.user_agent, |
232 | + request.content_type, |
233 | + response.content_type, |
234 | + context=ctxt) |
235 | + |
236 | + |
237 | +class Lockout(wsgi.Middleware): |
238 | + """Lockout for x minutes on y failed auths in a z minute period. |
239 | + |
240 | + x = lockout_timeout flag |
241 | + y = lockout_window flag |
242 | + z = lockout_attempts flag |
243 | + |
244 | + Uses memcached if lockout_memcached_servers flag is set, otherwise it |
245 | + uses a very simple in-proccess cache. Due to the simplicity of |
246 | + the implementation, the timeout window is started with the first |
247 | + failed request, so it will block if there are x failed logins within |
248 | + that period. |
249 | + |
250 | + There is a possible race condition where simultaneous requests could |
251 | + sneak in before the lockout hits, but this is extremely rare and would |
252 | + only result in a couple of extra failed attempts.""" |
253 | + |
254 | + def __init__(self, application): |
255 | + """middleware can use fake for testing.""" |
256 | + if FLAGS.memcached_servers: |
257 | + import memcache |
258 | + else: |
259 | + from nova import fakememcache as memcache |
260 | + self.mc = memcache.Client(FLAGS.memcached_servers, |
261 | + debug=0) |
262 | + super(Lockout, self).__init__(application) |
263 | + |
264 | + @webob.dec.wsgify(RequestClass=wsgi.Request) |
265 | + def __call__(self, req): |
266 | + access_key = str(req.params['AWSAccessKeyId']) |
267 | + failures_key = "authfailures-%s" % access_key |
268 | + failures = int(self.mc.get(failures_key) or 0) |
269 | + if failures >= FLAGS.lockout_attempts: |
270 | + detail = _("Too many failed authentications.") |
271 | + raise webob.exc.HTTPForbidden(detail=detail) |
272 | + res = req.get_response(self.application) |
273 | + if res.status_int == 403: |
274 | + failures = self.mc.incr(failures_key) |
275 | + if failures is None: |
276 | + # NOTE(vish): To use incr, failures has to be a string. |
277 | + self.mc.set(failures_key, '1', time=FLAGS.lockout_window * 60) |
278 | + elif failures >= FLAGS.lockout_attempts: |
279 | + lock_mins = FLAGS.lockout_minutes |
280 | + msg = _('Access key %(access_key)s has had %(failures)d' |
281 | + ' failed authentications and will be locked out' |
282 | + ' for %(lock_mins)d minutes.') % locals() |
283 | + LOG.warn(msg) |
284 | + self.mc.set(failures_key, str(failures), |
285 | + time=FLAGS.lockout_minutes * 60) |
286 | + return res |
287 | + |
288 | + |
289 | +class NoAuth(wsgi.Middleware): |
290 | + """Add user:project as 'nova.context' to WSGI environ.""" |
291 | + |
292 | + @webob.dec.wsgify(RequestClass=wsgi.Request) |
293 | + def __call__(self, req): |
294 | + if 'AWSAccessKeyId' not in req.params: |
295 | + raise webob.exc.HTTPBadRequest() |
296 | + user_id, _sep, project_id = req.params['AWSAccessKeyId'].partition(':') |
297 | + project_id = project_id or user_id |
298 | + remote_address = getattr(req, 'remote_address', '127.0.0.1') |
299 | + if FLAGS.use_forwarded_for: |
300 | + remote_address = req.headers.get('X-Forwarded-For', remote_address) |
301 | + ctx = context.RequestContext(user_id, |
302 | + project_id, |
303 | + is_admin=True, |
304 | + remote_address=remote_address) |
305 | + |
306 | + req.environ['nova.context'] = ctx |
307 | + return self.application |
308 | + |
309 | + |
310 | +class Authenticate(wsgi.Middleware): |
311 | + """Authenticate an EC2 request and add 'nova.context' to WSGI environ.""" |
312 | + |
313 | + @webob.dec.wsgify(RequestClass=wsgi.Request) |
314 | + def __call__(self, req): |
315 | + # Read request signature and access id. |
316 | + try: |
317 | + signature = req.params['Signature'] |
318 | + access = req.params['AWSAccessKeyId'] |
319 | + except KeyError: |
320 | + raise webob.exc.HTTPBadRequest() |
321 | + |
322 | + # Make a copy of args for authentication and signature verification. |
323 | + auth_params = dict(req.params) |
324 | + # Not part of authentication args |
325 | + auth_params.pop('Signature') |
326 | + |
327 | + # Authenticate the request. |
328 | + authman = manager.AuthManager() |
329 | + try: |
330 | + (user, project) = authman.authenticate( |
331 | + access, |
332 | + signature, |
333 | + auth_params, |
334 | + req.method, |
335 | + req.host, |
336 | + req.path) |
337 | + # Be explicit for what exceptions are 403, the rest bubble as 500 |
338 | + except (exception.NotFound, exception.NotAuthorized) as ex: |
339 | + LOG.audit(_("Authentication Failure: %s"), unicode(ex)) |
340 | + raise webob.exc.HTTPForbidden() |
341 | + |
342 | + # Authenticated! |
343 | + remote_address = req.remote_addr |
344 | + if FLAGS.use_forwarded_for: |
345 | + remote_address = req.headers.get('X-Forwarded-For', remote_address) |
346 | + roles = authman.get_active_roles(user, project) |
347 | + ctxt = context.RequestContext(user_id=user.id, |
348 | + project_id=project.id, |
349 | + is_admin=user.is_admin(), |
350 | + roles=roles, |
351 | + remote_address=remote_address) |
352 | + req.environ['nova.context'] = ctxt |
353 | + uname = user.name |
354 | + pname = project.name |
355 | + msg = _('Authenticated Request For %(uname)s:%(pname)s)') % locals() |
356 | + LOG.audit(msg, context=req.environ['nova.context']) |
357 | + return self.application |
358 | + |
359 | + |
360 | +class Requestify(wsgi.Middleware): |
361 | + |
362 | + def __init__(self, app, controller): |
363 | + super(Requestify, self).__init__(app) |
364 | + self.controller = utils.import_class(controller)() |
365 | + |
366 | + @webob.dec.wsgify(RequestClass=wsgi.Request) |
367 | + def __call__(self, req): |
368 | + non_args = ['Action', 'Signature', 'AWSAccessKeyId', 'SignatureMethod', |
369 | + 'SignatureVersion', 'Version', 'Timestamp'] |
370 | + args = dict(req.params) |
371 | + try: |
372 | + # Raise KeyError if omitted |
373 | + action = req.params['Action'] |
374 | + # Fix bug lp:720157 for older (version 1) clients |
375 | + version = req.params['SignatureVersion'] |
376 | + if int(version) == 1: |
377 | + non_args.remove('SignatureMethod') |
378 | + if 'SignatureMethod' in args: |
379 | + args.pop('SignatureMethod') |
380 | + for non_arg in non_args: |
381 | + # Remove, but raise KeyError if omitted |
382 | + args.pop(non_arg) |
383 | + except KeyError, e: |
384 | + raise webob.exc.HTTPBadRequest() |
385 | + |
386 | + LOG.debug(_('action: %s'), action) |
387 | + for key, value in args.items(): |
388 | + LOG.debug(_('arg: %(key)s\t\tval: %(value)s') % locals()) |
389 | + |
390 | + # Success! |
391 | + api_request = apirequest.APIRequest(self.controller, action, |
392 | + req.params['Version'], args) |
393 | + req.environ['ec2.request'] = api_request |
394 | + req.environ['ec2.action_args'] = args |
395 | + return self.application |
396 | + |
397 | + |
398 | +class Authorizer(wsgi.Middleware): |
399 | + |
400 | + """Authorize an EC2 API request. |
401 | + |
402 | + Return a 401 if ec2.controller and ec2.action in WSGI environ may not be |
403 | + executed in nova.context. |
404 | + """ |
405 | + |
406 | + def __init__(self, application): |
407 | + super(Authorizer, self).__init__(application) |
408 | + self.action_roles = { |
409 | + 'CloudController': { |
410 | + 'DescribeAvailabilityZones': ['all'], |
411 | + 'DescribeRegions': ['all'], |
412 | + 'DescribeSnapshots': ['all'], |
413 | + 'DescribeKeyPairs': ['all'], |
414 | + 'CreateKeyPair': ['all'], |
415 | + 'DeleteKeyPair': ['all'], |
416 | + 'DescribeSecurityGroups': ['all'], |
417 | + 'ImportPublicKey': ['all'], |
418 | + 'AuthorizeSecurityGroupIngress': ['netadmin'], |
419 | + 'RevokeSecurityGroupIngress': ['netadmin'], |
420 | + 'CreateSecurityGroup': ['netadmin'], |
421 | + 'DeleteSecurityGroup': ['netadmin'], |
422 | + 'GetConsoleOutput': ['projectmanager', 'sysadmin'], |
423 | + 'DescribeVolumes': ['projectmanager', 'sysadmin'], |
424 | + 'CreateVolume': ['projectmanager', 'sysadmin'], |
425 | + 'AttachVolume': ['projectmanager', 'sysadmin'], |
426 | + 'DetachVolume': ['projectmanager', 'sysadmin'], |
427 | + 'DescribeInstances': ['all'], |
428 | + 'DescribeAddresses': ['all'], |
429 | + 'AllocateAddress': ['netadmin'], |
430 | + 'ReleaseAddress': ['netadmin'], |
431 | + 'AssociateAddress': ['netadmin'], |
432 | + 'DisassociateAddress': ['netadmin'], |
433 | + 'RunInstances': ['projectmanager', 'sysadmin'], |
434 | + 'TerminateInstances': ['projectmanager', 'sysadmin'], |
435 | + 'RebootInstances': ['projectmanager', 'sysadmin'], |
436 | + 'UpdateInstance': ['projectmanager', 'sysadmin'], |
437 | + 'StartInstances': ['projectmanager', 'sysadmin'], |
438 | + 'StopInstances': ['projectmanager', 'sysadmin'], |
439 | + 'DeleteVolume': ['projectmanager', 'sysadmin'], |
440 | + 'DescribeImages': ['all'], |
441 | + 'DeregisterImage': ['projectmanager', 'sysadmin'], |
442 | + 'RegisterImage': ['projectmanager', 'sysadmin'], |
443 | + 'DescribeImageAttribute': ['all'], |
444 | + 'ModifyImageAttribute': ['projectmanager', 'sysadmin'], |
445 | + 'UpdateImage': ['projectmanager', 'sysadmin'], |
446 | + 'CreateImage': ['projectmanager', 'sysadmin'], |
447 | + }, |
448 | + 'AdminController': { |
449 | + # All actions have the same permission: ['none'] (the default) |
450 | + # superusers will be allowed to run them |
451 | + # all others will get HTTPUnauthorized. |
452 | + }, |
453 | + } |
454 | + |
455 | + @webob.dec.wsgify(RequestClass=wsgi.Request) |
456 | + def __call__(self, req): |
457 | + context = req.environ['nova.context'] |
458 | + controller = req.environ['ec2.request'].controller.__class__.__name__ |
459 | + action = req.environ['ec2.request'].action |
460 | + allowed_roles = self.action_roles[controller].get(action, ['none']) |
461 | + if self._matches_any_role(context, allowed_roles): |
462 | + return self.application |
463 | + else: |
464 | + LOG.audit(_('Unauthorized request for controller=%(controller)s ' |
465 | + 'and action=%(action)s') % locals(), context=context) |
466 | + raise webob.exc.HTTPUnauthorized() |
467 | + |
468 | + def _matches_any_role(self, context, roles): |
469 | + """Return True if any role in roles is allowed in context.""" |
470 | + if context.is_admin: |
471 | + return True |
472 | + if 'all' in roles: |
473 | + return True |
474 | + if 'none' in roles: |
475 | + return False |
476 | + return any(role in context.roles for role in roles) |
477 | + |
478 | + |
479 | +class Executor(wsgi.Application): |
480 | + |
481 | + """Execute an EC2 API request. |
482 | + |
483 | + Executes 'ec2.action' upon 'ec2.controller', passing 'nova.context' and |
484 | + 'ec2.action_args' (all variables in WSGI environ.) Returns an XML |
485 | + response, or a 400 upon failure. |
486 | + """ |
487 | + |
488 | + @webob.dec.wsgify(RequestClass=wsgi.Request) |
489 | + def __call__(self, req): |
490 | + context = req.environ['nova.context'] |
491 | + api_request = req.environ['ec2.request'] |
492 | + result = None |
493 | + try: |
494 | + result = api_request.invoke(context) |
495 | + except exception.InstanceNotFound as ex: |
496 | + LOG.info(_('InstanceNotFound raised: %s'), unicode(ex), |
497 | + context=context) |
498 | + ec2_id = ec2utils.id_to_ec2_id(ex.kwargs['instance_id']) |
499 | + message = ex.message % {'instance_id': ec2_id} |
500 | + return self._error(req, context, type(ex).__name__, message) |
501 | + except exception.VolumeNotFound as ex: |
502 | + LOG.info(_('VolumeNotFound raised: %s'), unicode(ex), |
503 | + context=context) |
504 | + ec2_id = ec2utils.id_to_ec2_vol_id(ex.kwargs['volume_id']) |
505 | + message = ex.message % {'volume_id': ec2_id} |
506 | + return self._error(req, context, type(ex).__name__, message) |
507 | + except exception.SnapshotNotFound as ex: |
508 | + LOG.info(_('SnapshotNotFound raised: %s'), unicode(ex), |
509 | + context=context) |
510 | + ec2_id = ec2utils.id_to_ec2_snap_id(ex.kwargs['snapshot_id']) |
511 | + message = ex.message % {'snapshot_id': ec2_id} |
512 | + return self._error(req, context, type(ex).__name__, message) |
513 | + except exception.NotFound as ex: |
514 | + LOG.info(_('NotFound raised: %s'), unicode(ex), context=context) |
515 | + return self._error(req, context, type(ex).__name__, unicode(ex)) |
516 | + except exception.ApiError as ex: |
517 | + LOG.exception(_('ApiError raised: %s'), unicode(ex), |
518 | + context=context) |
519 | + if ex.code: |
520 | + return self._error(req, context, ex.code, unicode(ex)) |
521 | + else: |
522 | + return self._error(req, context, type(ex).__name__, |
523 | + unicode(ex)) |
524 | + except exception.KeyPairExists as ex: |
525 | + LOG.debug(_('KeyPairExists raised: %s'), unicode(ex), |
526 | + context=context) |
527 | + return self._error(req, context, type(ex).__name__, unicode(ex)) |
528 | + except exception.InvalidParameterValue as ex: |
529 | + LOG.debug(_('InvalidParameterValue raised: %s'), unicode(ex), |
530 | + context=context) |
531 | + return self._error(req, context, type(ex).__name__, unicode(ex)) |
532 | + except exception.InvalidPortRange as ex: |
533 | + LOG.debug(_('InvalidPortRange raised: %s'), unicode(ex), |
534 | + context=context) |
535 | + return self._error(req, context, type(ex).__name__, unicode(ex)) |
536 | + except exception.NotAuthorized as ex: |
537 | + LOG.info(_('NotAuthorized raised: %s'), unicode(ex), |
538 | + context=context) |
539 | + return self._error(req, context, type(ex).__name__, unicode(ex)) |
540 | + except Exception as ex: |
541 | + extra = {'environment': req.environ} |
542 | + LOG.exception(_('Unexpected error raised: %s'), unicode(ex), |
543 | + extra=extra, context=context) |
544 | + return self._error(req, |
545 | + context, |
546 | + 'UnknownError', |
547 | + _('An unknown error has occurred. ' |
548 | + 'Please try your request again.')) |
549 | + else: |
550 | + resp = webob.Response() |
551 | + resp.status = 200 |
552 | + resp.headers['Content-Type'] = 'text/xml' |
553 | + resp.body = str(result) |
554 | + return resp |
555 | + |
556 | + def _error(self, req, context, code, message): |
557 | + LOG.error("%s: %s", code, message, context=context) |
558 | + resp = webob.Response() |
559 | + resp.status = 400 |
560 | + resp.headers['Content-Type'] = 'text/xml' |
561 | + resp.body = str('<?xml version="1.0"?>\n' |
562 | + '<Response><Errors><Error><Code>%s</Code>' |
563 | + '<Message>%s</Message></Error></Errors>' |
564 | + '<RequestID>%s</RequestID></Response>' % |
565 | + (utils.utf8(code), utils.utf8(message), |
566 | + utils.utf8(context.request_id))) |
567 | + return resp |
568 | + |
569 | + |
570 | +class Versions(wsgi.Application): |
571 | + |
572 | + @webob.dec.wsgify(RequestClass=wsgi.Request) |
573 | + def __call__(self, req): |
574 | + """Respond to a request for all EC2 versions.""" |
575 | + # available api versions |
576 | + versions = [ |
577 | + '1.0', |
578 | + '2007-01-19', |
579 | + '2007-03-01', |
580 | + '2007-08-29', |
581 | + '2007-10-10', |
582 | + '2007-12-15', |
583 | + '2008-02-01', |
584 | + '2008-09-01', |
585 | + '2009-04-04', |
586 | + ] |
587 | + return ''.join('%s\n' % v for v in versions) |
588 | |
589 | === added directory '.pc/security-fix-lp868360.patch/nova/auth' |
590 | === added file '.pc/security-fix-lp868360.patch/nova/auth/manager.py' |
591 | --- .pc/security-fix-lp868360.patch/nova/auth/manager.py 1970-01-01 00:00:00 +0000 |
592 | +++ .pc/security-fix-lp868360.patch/nova/auth/manager.py 2011-10-27 13:05:25 +0000 |
593 | @@ -0,0 +1,842 @@ |
594 | +# vim: tabstop=4 shiftwidth=4 softtabstop=4 |
595 | + |
596 | +# Copyright 2010 United States Government as represented by the |
597 | +# Administrator of the National Aeronautics and Space Administration. |
598 | +# All Rights Reserved. |
599 | +# |
600 | +# Licensed under the Apache License, Version 2.0 (the "License"); you may |
601 | +# not use this file except in compliance with the License. You may obtain |
602 | +# a copy of the License at |
603 | +# |
604 | +# http://www.apache.org/licenses/LICENSE-2.0 |
605 | +# |
606 | +# Unless required by applicable law or agreed to in writing, software |
607 | +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
608 | +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
609 | +# License for the specific language governing permissions and limitations |
610 | +# under the License. |
611 | + |
612 | +""" |
613 | +WARNING: This code is deprecated and will be removed. |
614 | +Keystone is the recommended solution for auth management. |
615 | + |
616 | +Nova authentication management |
617 | +""" |
618 | + |
619 | +import os |
620 | +import shutil |
621 | +import string # pylint: disable=W0402 |
622 | +import tempfile |
623 | +import uuid |
624 | +import zipfile |
625 | + |
626 | +from nova import context |
627 | +from nova import crypto |
628 | +from nova import db |
629 | +from nova import exception |
630 | +from nova import flags |
631 | +from nova import log as logging |
632 | +from nova import utils |
633 | +from nova.auth import signer |
634 | + |
635 | + |
636 | +FLAGS = flags.FLAGS |
637 | +flags.DEFINE_bool('use_deprecated_auth', |
638 | + False, |
639 | + 'This flag must be set to use old style auth') |
640 | + |
641 | +flags.DEFINE_list('allowed_roles', |
642 | + ['cloudadmin', 'itsec', 'sysadmin', 'netadmin', 'developer'], |
643 | + 'Allowed roles for project') |
644 | +# NOTE(vish): a user with one of these roles will be a superuser and |
645 | +# have access to all api commands |
646 | +flags.DEFINE_list('superuser_roles', ['cloudadmin'], |
647 | + 'Roles that ignore authorization checking completely') |
648 | + |
649 | +# NOTE(vish): a user with one of these roles will have it for every |
650 | +# project, even if he or she is not a member of the project |
651 | +flags.DEFINE_list('global_roles', ['cloudadmin', 'itsec'], |
652 | + 'Roles that apply to all projects') |
653 | + |
654 | +flags.DEFINE_string('credentials_template', |
655 | + utils.abspath('auth/novarc.template'), |
656 | + 'Template for creating users rc file') |
657 | +flags.DEFINE_string('vpn_client_template', |
658 | + utils.abspath('cloudpipe/client.ovpn.template'), |
659 | + 'Template for creating users vpn file') |
660 | +flags.DEFINE_string('credential_vpn_file', 'nova-vpn.conf', |
661 | + 'Filename of certificate in credentials zip') |
662 | +flags.DEFINE_string('credential_key_file', 'pk.pem', |
663 | + 'Filename of private key in credentials zip') |
664 | +flags.DEFINE_string('credential_cert_file', 'cert.pem', |
665 | + 'Filename of certificate in credentials zip') |
666 | +flags.DEFINE_string('credential_rc_file', '%src', |
667 | + 'Filename of rc in credentials zip, %s will be ' |
668 | + 'replaced by name of the region (nova by default)') |
669 | +flags.DEFINE_string('auth_driver', 'nova.auth.dbdriver.DbDriver', |
670 | + 'Driver that auth manager uses') |
671 | + |
672 | +LOG = logging.getLogger('nova.auth.manager') |
673 | + |
674 | + |
675 | +if FLAGS.memcached_servers: |
676 | + import memcache |
677 | +else: |
678 | + from nova import fakememcache as memcache |
679 | + |
680 | + |
681 | +class AuthBase(object): |
682 | + """Base class for objects relating to auth |
683 | + |
684 | + Objects derived from this class should be stupid data objects with |
685 | + an id member. They may optionally contain methods that delegate to |
686 | + AuthManager, but should not implement logic themselves. |
687 | + """ |
688 | + |
689 | + @classmethod |
690 | + def safe_id(cls, obj): |
691 | + """Safely get object id. |
692 | + |
693 | + This method will return the id of the object if the object |
694 | + is of this class, otherwise it will return the original object. |
695 | + This allows methods to accept objects or ids as paramaters. |
696 | + """ |
697 | + if isinstance(obj, cls): |
698 | + return obj.id |
699 | + else: |
700 | + return obj |
701 | + |
702 | + |
703 | +class User(AuthBase): |
704 | + """Object representing a user |
705 | + |
706 | + The following attributes are defined: |
707 | + :id: A system identifier for the user. A string (for LDAP) |
708 | + :name: The user name, potentially in some more friendly format |
709 | + :access: The 'username' for EC2 authentication |
710 | + :secret: The 'password' for EC2 authenticatoin |
711 | + :admin: ??? |
712 | + """ |
713 | + |
714 | + def __init__(self, id, name, access, secret, admin): |
715 | + AuthBase.__init__(self) |
716 | + assert isinstance(id, basestring) |
717 | + self.id = id |
718 | + self.name = name |
719 | + self.access = access |
720 | + self.secret = secret |
721 | + self.admin = admin |
722 | + |
723 | + def is_superuser(self): |
724 | + return AuthManager().is_superuser(self) |
725 | + |
726 | + def is_admin(self): |
727 | + return AuthManager().is_admin(self) |
728 | + |
729 | + def has_role(self, role): |
730 | + return AuthManager().has_role(self, role) |
731 | + |
732 | + def add_role(self, role): |
733 | + return AuthManager().add_role(self, role) |
734 | + |
735 | + def remove_role(self, role): |
736 | + return AuthManager().remove_role(self, role) |
737 | + |
738 | + def is_project_member(self, project): |
739 | + return AuthManager().is_project_member(self, project) |
740 | + |
741 | + def is_project_manager(self, project): |
742 | + return AuthManager().is_project_manager(self, project) |
743 | + |
744 | + def __repr__(self): |
745 | + return "User('%s', '%s', '%s', '%s', %s)" % (self.id, |
746 | + self.name, |
747 | + self.access, |
748 | + self.secret, |
749 | + self.admin) |
750 | + |
751 | + |
752 | +class Project(AuthBase): |
753 | + """Represents a Project returned from the datastore""" |
754 | + |
755 | + def __init__(self, id, name, project_manager_id, description, member_ids): |
756 | + AuthBase.__init__(self) |
757 | + self.id = id |
758 | + self.name = name |
759 | + self.project_manager_id = project_manager_id |
760 | + self.description = description |
761 | + self.member_ids = member_ids |
762 | + |
763 | + @property |
764 | + def project_manager(self): |
765 | + return AuthManager().get_user(self.project_manager_id) |
766 | + |
767 | + @property |
768 | + def vpn_ip(self): |
769 | + ip, _port = AuthManager().get_project_vpn_data(self) |
770 | + return ip |
771 | + |
772 | + @property |
773 | + def vpn_port(self): |
774 | + _ip, port = AuthManager().get_project_vpn_data(self) |
775 | + return port |
776 | + |
777 | + def has_manager(self, user): |
778 | + return AuthManager().is_project_manager(user, self) |
779 | + |
780 | + def has_member(self, user): |
781 | + return AuthManager().is_project_member(user, self) |
782 | + |
783 | + def add_role(self, user, role): |
784 | + return AuthManager().add_role(user, role, self) |
785 | + |
786 | + def remove_role(self, user, role): |
787 | + return AuthManager().remove_role(user, role, self) |
788 | + |
789 | + def has_role(self, user, role): |
790 | + return AuthManager().has_role(user, role, self) |
791 | + |
792 | + def get_credentials(self, user): |
793 | + return AuthManager().get_credentials(user, self) |
794 | + |
795 | + def __repr__(self): |
796 | + return "Project('%s', '%s', '%s', '%s', %s)" % \ |
797 | + (self.id, self.name, self.project_manager_id, self.description, |
798 | + self.member_ids) |
799 | + |
800 | + |
801 | +class AuthManager(object): |
802 | + """Manager Singleton for dealing with Users, Projects, and Keypairs |
803 | + |
804 | + Methods accept objects or ids. |
805 | + |
806 | + AuthManager uses a driver object to make requests to the data backend. |
807 | + See ldapdriver for reference. |
808 | + |
809 | + AuthManager also manages associated data related to Auth objects that |
810 | + need to be more accessible, such as vpn ips and ports. |
811 | + """ |
812 | + |
813 | + _instance = None |
814 | + mc = None |
815 | + |
816 | + def __new__(cls, *args, **kwargs): |
817 | + """Returns the AuthManager singleton""" |
818 | + if not cls._instance or ('new' in kwargs and kwargs['new']): |
819 | + cls._instance = super(AuthManager, cls).__new__(cls) |
820 | + return cls._instance |
821 | + |
822 | + def __init__(self, driver=None, *args, **kwargs): |
823 | + """Inits the driver from parameter or flag |
824 | + |
825 | + __init__ is run every time AuthManager() is called, so we only |
826 | + reset the driver if it is not set or a new driver is specified. |
827 | + """ |
828 | + self.network_manager = utils.import_object(FLAGS.network_manager) |
829 | + if driver or not getattr(self, 'driver', None): |
830 | + self.driver = utils.import_class(driver or FLAGS.auth_driver) |
831 | + if AuthManager.mc is None: |
832 | + AuthManager.mc = memcache.Client(FLAGS.memcached_servers, debug=0) |
833 | + |
834 | + def authenticate(self, access, signature, params, verb='GET', |
835 | + server_string='127.0.0.1:8773', path='/', |
836 | + check_type='ec2', headers=None): |
837 | + """Authenticates AWS request using access key and signature |
838 | + |
839 | + If the project is not specified, attempts to authenticate to |
840 | + a project with the same name as the user. This way, older tools |
841 | + that have no project knowledge will still work. |
842 | + |
843 | + @type access: str |
844 | + @param access: Access key for user in the form "access:project". |
845 | + |
846 | + @type signature: str |
847 | + @param signature: Signature of the request. |
848 | + |
849 | + @type params: list of str |
850 | + @param params: Web paramaters used for the signature. |
851 | + |
852 | + @type verb: str |
853 | + @param verb: Web request verb ('GET' or 'POST'). |
854 | + |
855 | + @type server_string: str |
856 | + @param server_string: Web request server string. |
857 | + |
858 | + @type path: str |
859 | + @param path: Web request path. |
860 | + |
861 | + @type check_type: str |
862 | + @param check_type: Type of signature to check. 'ec2' for EC2, 's3' for |
863 | + S3. Any other value will cause signature not to be |
864 | + checked. |
865 | + |
866 | + @type headers: list |
867 | + @param headers: HTTP headers passed with the request (only needed for |
868 | + s3 signature checks) |
869 | + |
870 | + @rtype: tuple (User, Project) |
871 | + @return: User and project that the request represents. |
872 | + """ |
873 | + # TODO(vish): check for valid timestamp |
874 | + (access_key, _sep, project_id) = access.partition(':') |
875 | + |
876 | + LOG.debug(_('Looking up user: %r'), access_key) |
877 | + user = self.get_user_from_access_key(access_key) |
878 | + LOG.debug('user: %r', user) |
879 | + if user is None: |
880 | + LOG.audit(_("Failed authorization for access key %s"), access_key) |
881 | + raise exception.AccessKeyNotFound(access_key=access_key) |
882 | + |
883 | + # NOTE(vish): if we stop using project name as id we need better |
884 | + # logic to find a default project for user |
885 | + if project_id == '': |
886 | + LOG.debug(_("Using project name = user name (%s)"), user.name) |
887 | + project_id = user.name |
888 | + |
889 | + project = self.get_project(project_id) |
890 | + if project is None: |
891 | + pjid = project_id |
892 | + uname = user.name |
893 | + LOG.audit(_("failed authorization: no project named %(pjid)s" |
894 | + " (user=%(uname)s)") % locals()) |
895 | + raise exception.ProjectNotFound(project_id=project_id) |
896 | + if not self.is_admin(user) and not self.is_project_member(user, |
897 | + project): |
898 | + uname = user.name |
899 | + uid = user.id |
900 | + pjname = project.name |
901 | + pjid = project.id |
902 | + LOG.audit(_("Failed authorization: user %(uname)s not admin" |
903 | + " and not member of project %(pjname)s") % locals()) |
904 | + raise exception.ProjectMembershipNotFound(project_id=pjid, |
905 | + user_id=uid) |
906 | + if check_type == 's3': |
907 | + sign = signer.Signer(user.secret.encode()) |
908 | + expected_signature = sign.s3_authorization(headers, verb, path) |
909 | + LOG.debug(_('user.secret: %s'), user.secret) |
910 | + LOG.debug(_('expected_signature: %s'), expected_signature) |
911 | + LOG.debug(_('signature: %s'), signature) |
912 | + if signature != expected_signature: |
913 | + LOG.audit(_("Invalid signature for user %s"), user.name) |
914 | + raise exception.InvalidSignature(signature=signature, |
915 | + user=user) |
916 | + elif check_type == 'ec2': |
917 | + # NOTE(vish): hmac can't handle unicode, so encode ensures that |
918 | + # secret isn't unicode |
919 | + expected_signature = signer.Signer(user.secret.encode()).generate( |
920 | + params, verb, server_string, path) |
921 | + LOG.debug(_('user.secret: %s'), user.secret) |
922 | + LOG.debug(_('expected_signature: %s'), expected_signature) |
923 | + LOG.debug(_('signature: %s'), signature) |
924 | + if signature != expected_signature: |
925 | + (addr_str, port_str) = utils.parse_server_string(server_string) |
926 | + # If the given server_string contains port num, try without it. |
927 | + if port_str != '': |
928 | + host_only_signature = signer.Signer( |
929 | + user.secret.encode()).generate(params, verb, |
930 | + addr_str, path) |
931 | + LOG.debug(_('host_only_signature: %s'), |
932 | + host_only_signature) |
933 | + if signature == host_only_signature: |
934 | + return (user, project) |
935 | + LOG.audit(_("Invalid signature for user %s"), user.name) |
936 | + raise exception.InvalidSignature(signature=signature, |
937 | + user=user) |
938 | + return (user, project) |
939 | + |
940 | + def get_access_key(self, user, project): |
941 | + """Get an access key that includes user and project""" |
942 | + if not isinstance(user, User): |
943 | + user = self.get_user(user) |
944 | + return "%s:%s" % (user.access, Project.safe_id(project)) |
945 | + |
946 | + def is_superuser(self, user): |
947 | + """Checks for superuser status, allowing user to bypass authorization |
948 | + |
949 | + @type user: User or uid |
950 | + @param user: User to check. |
951 | + |
952 | + @rtype: bool |
953 | + @return: True for superuser. |
954 | + """ |
955 | + if not isinstance(user, User): |
956 | + user = self.get_user(user) |
957 | + # NOTE(vish): admin flag on user represents superuser |
958 | + if user.admin: |
959 | + return True |
960 | + for role in FLAGS.superuser_roles: |
961 | + if self.has_role(user, role): |
962 | + return True |
963 | + |
964 | + def is_admin(self, user): |
965 | + """Checks for admin status, allowing user to access all projects |
966 | + |
967 | + @type user: User or uid |
968 | + @param user: User to check. |
969 | + |
970 | + @rtype: bool |
971 | + @return: True for admin. |
972 | + """ |
973 | + if not isinstance(user, User): |
974 | + user = self.get_user(user) |
975 | + if self.is_superuser(user): |
976 | + return True |
977 | + for role in FLAGS.global_roles: |
978 | + if self.has_role(user, role): |
979 | + return True |
980 | + |
981 | + def _build_mc_key(self, user, role, project=None): |
982 | + key_parts = ['rolecache', User.safe_id(user), str(role)] |
983 | + if project: |
984 | + key_parts.append(Project.safe_id(project)) |
985 | + return '-'.join(key_parts) |
986 | + |
987 | + def _clear_mc_key(self, user, role, project=None): |
988 | + # NOTE(anthony): it would be better to delete the key |
989 | + self.mc.set(self._build_mc_key(user, role, project), None) |
990 | + |
991 | + def _has_role(self, user, role, project=None): |
992 | + mc_key = self._build_mc_key(user, role, project) |
993 | + rslt = self.mc.get(mc_key) |
994 | + if rslt is None: |
995 | + with self.driver() as drv: |
996 | + rslt = drv.has_role(user, role, project) |
997 | + self.mc.set(mc_key, rslt) |
998 | + return rslt |
999 | + else: |
1000 | + return rslt |
1001 | + |
1002 | + def has_role(self, user, role, project=None): |
1003 | + """Checks existence of role for user |
1004 | + |
1005 | + If project is not specified, checks for a global role. If project |
1006 | + is specified, checks for the union of the global role and the |
1007 | + project role. |
1008 | + |
1009 | + Role 'projectmanager' only works for projects and simply checks to |
1010 | + see if the user is the project_manager of the specified project. It |
1011 | + is the same as calling is_project_manager(user, project). |
1012 | + |
1013 | + @type user: User or uid |
1014 | + @param user: User to check. |
1015 | + |
1016 | + @type role: str |
1017 | + @param role: Role to check. |
1018 | + |
1019 | + @type project: Project or project_id |
1020 | + @param project: Project in which to look for local role. |
1021 | + |
1022 | + @rtype: bool |
1023 | + @return: True if the user has the role. |
1024 | + """ |
1025 | + if role == 'projectmanager': |
1026 | + if not project: |
1027 | + raise exception.Error(_("Must specify project")) |
1028 | + return self.is_project_manager(user, project) |
1029 | + |
1030 | + global_role = self._has_role(User.safe_id(user), |
1031 | + role, |
1032 | + None) |
1033 | + |
1034 | + if not global_role: |
1035 | + return global_role |
1036 | + |
1037 | + if not project or role in FLAGS.global_roles: |
1038 | + return global_role |
1039 | + |
1040 | + return self._has_role(User.safe_id(user), |
1041 | + role, |
1042 | + Project.safe_id(project)) |
1043 | + |
1044 | + def add_role(self, user, role, project=None): |
1045 | + """Adds role for user |
1046 | + |
1047 | + If project is not specified, adds a global role. If project |
1048 | + is specified, adds a local role. |
1049 | + |
1050 | + The 'projectmanager' role is special and can't be added or removed. |
1051 | + |
1052 | + @type user: User or uid |
1053 | + @param user: User to which to add role. |
1054 | + |
1055 | + @type role: str |
1056 | + @param role: Role to add. |
1057 | + |
1058 | + @type project: Project or project_id |
1059 | + @param project: Project in which to add local role. |
1060 | + """ |
1061 | + if role not in FLAGS.allowed_roles: |
1062 | + raise exception.UserRoleNotFound(role_id=role) |
1063 | + if project is not None and role in FLAGS.global_roles: |
1064 | + raise exception.GlobalRoleNotAllowed(role_id=role) |
1065 | + uid = User.safe_id(user) |
1066 | + pid = Project.safe_id(project) |
1067 | + if project: |
1068 | + LOG.audit(_("Adding role %(role)s to user %(uid)s" |
1069 | + " in project %(pid)s") % locals()) |
1070 | + else: |
1071 | + LOG.audit(_("Adding sitewide role %(role)s to user %(uid)s") |
1072 | + % locals()) |
1073 | + with self.driver() as drv: |
1074 | + self._clear_mc_key(uid, role, pid) |
1075 | + drv.add_role(uid, role, pid) |
1076 | + |
1077 | + def remove_role(self, user, role, project=None): |
1078 | + """Removes role for user |
1079 | + |
1080 | + If project is not specified, removes a global role. If project |
1081 | + is specified, removes a local role. |
1082 | + |
1083 | + The 'projectmanager' role is special and can't be added or removed. |
1084 | + |
1085 | + @type user: User or uid |
1086 | + @param user: User from which to remove role. |
1087 | + |
1088 | + @type role: str |
1089 | + @param role: Role to remove. |
1090 | + |
1091 | + @type project: Project or project_id |
1092 | + @param project: Project in which to remove local role. |
1093 | + """ |
1094 | + uid = User.safe_id(user) |
1095 | + pid = Project.safe_id(project) |
1096 | + if project: |
1097 | + LOG.audit(_("Removing role %(role)s from user %(uid)s" |
1098 | + " on project %(pid)s") % locals()) |
1099 | + else: |
1100 | + LOG.audit(_("Removing sitewide role %(role)s" |
1101 | + " from user %(uid)s") % locals()) |
1102 | + with self.driver() as drv: |
1103 | + self._clear_mc_key(uid, role, pid) |
1104 | + drv.remove_role(uid, role, pid) |
1105 | + |
1106 | + @staticmethod |
1107 | + def get_roles(project_roles=True): |
1108 | + """Get list of allowed roles""" |
1109 | + if project_roles: |
1110 | + return list(set(FLAGS.allowed_roles) - set(FLAGS.global_roles)) |
1111 | + else: |
1112 | + return FLAGS.allowed_roles |
1113 | + |
1114 | + def get_user_roles(self, user, project=None): |
1115 | + """Get user global or per-project roles""" |
1116 | + with self.driver() as drv: |
1117 | + return drv.get_user_roles(User.safe_id(user), |
1118 | + Project.safe_id(project)) |
1119 | + |
1120 | + def get_active_roles(self, user, project=None): |
1121 | + """Get all active roles for context""" |
1122 | + if project: |
1123 | + roles = FLAGS.allowed_roles + ['projectmanager'] |
1124 | + else: |
1125 | + roles = FLAGS.global_roles |
1126 | + return [role for role in roles if self.has_role(user, role, project)] |
1127 | + |
1128 | + def get_project(self, pid): |
1129 | + """Get project object by id""" |
1130 | + with self.driver() as drv: |
1131 | + project_dict = drv.get_project(pid) |
1132 | + if project_dict: |
1133 | + return Project(**project_dict) |
1134 | + |
1135 | + def get_projects(self, user=None): |
1136 | + """Retrieves list of projects, optionally filtered by user""" |
1137 | + with self.driver() as drv: |
1138 | + project_list = drv.get_projects(User.safe_id(user)) |
1139 | + if not project_list: |
1140 | + return [] |
1141 | + return [Project(**project_dict) for project_dict in project_list] |
1142 | + |
1143 | + def create_project(self, name, manager_user, description=None, |
1144 | + member_users=None): |
1145 | + """Create a project |
1146 | + |
1147 | + @type name: str |
1148 | + @param name: Name of the project to create. The name will also be |
1149 | + used as the project id. |
1150 | + |
1151 | + @type manager_user: User or uid |
1152 | + @param manager_user: This user will be the project manager. |
1153 | + |
1154 | + @type description: str |
1155 | + @param project: Description of the project. If no description is |
1156 | + specified, the name of the project will be used. |
1157 | + |
1158 | + @type member_users: list of User or uid |
1159 | + @param: Initial project members. The project manager will always be |
1160 | + added as a member, even if he isn't specified in this list. |
1161 | + |
1162 | + @rtype: Project |
1163 | + @return: The new project. |
1164 | + """ |
1165 | + if member_users: |
1166 | + member_users = [User.safe_id(u) for u in member_users] |
1167 | + with self.driver() as drv: |
1168 | + project_dict = drv.create_project(name, |
1169 | + User.safe_id(manager_user), |
1170 | + description, |
1171 | + member_users) |
1172 | + if project_dict: |
1173 | + LOG.audit(_("Created project %(name)s with" |
1174 | + " manager %(manager_user)s") % locals()) |
1175 | + project = Project(**project_dict) |
1176 | + return project |
1177 | + |
1178 | + def modify_project(self, project, manager_user=None, description=None): |
1179 | + """Modify a project |
1180 | + |
1181 | + @type name: Project or project_id |
1182 | + @param project: The project to modify. |
1183 | + |
1184 | + @type manager_user: User or uid |
1185 | + @param manager_user: This user will be the new project manager. |
1186 | + |
1187 | + @type description: str |
1188 | + @param project: This will be the new description of the project. |
1189 | + |
1190 | + """ |
1191 | + LOG.audit(_("modifying project %s"), Project.safe_id(project)) |
1192 | + if manager_user: |
1193 | + manager_user = User.safe_id(manager_user) |
1194 | + with self.driver() as drv: |
1195 | + drv.modify_project(Project.safe_id(project), |
1196 | + manager_user, |
1197 | + description) |
1198 | + |
1199 | + def add_to_project(self, user, project): |
1200 | + """Add user to project""" |
1201 | + uid = User.safe_id(user) |
1202 | + pid = Project.safe_id(project) |
1203 | + LOG.audit(_("Adding user %(uid)s to project %(pid)s") % locals()) |
1204 | + with self.driver() as drv: |
1205 | + return drv.add_to_project(User.safe_id(user), |
1206 | + Project.safe_id(project)) |
1207 | + |
1208 | + def is_project_manager(self, user, project): |
1209 | + """Checks if user is project manager""" |
1210 | + if not isinstance(project, Project): |
1211 | + project = self.get_project(project) |
1212 | + return User.safe_id(user) == project.project_manager_id |
1213 | + |
1214 | + def is_project_member(self, user, project): |
1215 | + """Checks to see if user is a member of project""" |
1216 | + if not isinstance(project, Project): |
1217 | + project = self.get_project(project) |
1218 | + return User.safe_id(user) in project.member_ids |
1219 | + |
1220 | + def remove_from_project(self, user, project): |
1221 | + """Removes a user from a project""" |
1222 | + uid = User.safe_id(user) |
1223 | + pid = Project.safe_id(project) |
1224 | + LOG.audit(_("Remove user %(uid)s from project %(pid)s") % locals()) |
1225 | + with self.driver() as drv: |
1226 | + return drv.remove_from_project(uid, pid) |
1227 | + |
1228 | + @staticmethod |
1229 | + def get_project_vpn_data(project): |
1230 | + """Gets vpn ip and port for project |
1231 | + |
1232 | + @type project: Project or project_id |
1233 | + @param project: Project from which to get associated vpn data |
1234 | + |
1235 | + @rvalue: tuple of (str, str) |
1236 | + @return: A tuple containing (ip, port) or None, None if vpn has |
1237 | + not been allocated for user. |
1238 | + """ |
1239 | + |
1240 | + networks = db.project_get_networks(context.get_admin_context(), |
1241 | + Project.safe_id(project), False) |
1242 | + if not networks: |
1243 | + return (None, None) |
1244 | + |
1245 | + # TODO(tr3buchet): not sure what you guys plan on doing with this |
1246 | + # but it's possible for a project to have multiple sets of vpn data |
1247 | + # for now I'm just returning the first one |
1248 | + network = networks[0] |
1249 | + return (network['vpn_public_address'], |
1250 | + network['vpn_public_port']) |
1251 | + |
1252 | + def delete_project(self, project): |
1253 | + """Deletes a project""" |
1254 | + LOG.audit(_("Deleting project %s"), Project.safe_id(project)) |
1255 | + with self.driver() as drv: |
1256 | + drv.delete_project(Project.safe_id(project)) |
1257 | + |
1258 | + def get_user(self, uid): |
1259 | + """Retrieves a user by id""" |
1260 | + with self.driver() as drv: |
1261 | + user_dict = drv.get_user(uid) |
1262 | + if user_dict: |
1263 | + return User(**user_dict) |
1264 | + |
1265 | + def get_user_from_access_key(self, access_key): |
1266 | + """Retrieves a user by access key""" |
1267 | + with self.driver() as drv: |
1268 | + user_dict = drv.get_user_from_access_key(access_key) |
1269 | + if user_dict: |
1270 | + return User(**user_dict) |
1271 | + |
1272 | + def get_users(self): |
1273 | + """Retrieves a list of all users""" |
1274 | + with self.driver() as drv: |
1275 | + user_list = drv.get_users() |
1276 | + if not user_list: |
1277 | + return [] |
1278 | + return [User(**user_dict) for user_dict in user_list] |
1279 | + |
1280 | + def create_user(self, name, access=None, secret=None, admin=False): |
1281 | + """Creates a user |
1282 | + |
1283 | + @type name: str |
1284 | + @param name: Name of the user to create. |
1285 | + |
1286 | + @type access: str |
1287 | + @param access: Access Key (defaults to a random uuid) |
1288 | + |
1289 | + @type secret: str |
1290 | + @param secret: Secret Key (defaults to a random uuid) |
1291 | + |
1292 | + @type admin: bool |
1293 | + @param admin: Whether to set the admin flag. The admin flag gives |
1294 | + superuser status regardless of roles specifed for the user. |
1295 | + |
1296 | + @type create_project: bool |
1297 | + @param: Whether to create a project for the user with the same name. |
1298 | + |
1299 | + @rtype: User |
1300 | + @return: The new user. |
1301 | + """ |
1302 | + if access is None: |
1303 | + access = str(uuid.uuid4()) |
1304 | + if secret is None: |
1305 | + secret = str(uuid.uuid4()) |
1306 | + with self.driver() as drv: |
1307 | + user_dict = drv.create_user(name, access, secret, admin) |
1308 | + if user_dict: |
1309 | + rv = User(**user_dict) |
1310 | + rvname = rv.name |
1311 | + rvadmin = rv.admin |
1312 | + LOG.audit(_("Created user %(rvname)s" |
1313 | + " (admin: %(rvadmin)r)") % locals()) |
1314 | + return rv |
1315 | + |
1316 | + def delete_user(self, user): |
1317 | + """Deletes a user |
1318 | + |
1319 | + Additionally deletes all users key_pairs""" |
1320 | + uid = User.safe_id(user) |
1321 | + LOG.audit(_("Deleting user %s"), uid) |
1322 | + db.key_pair_destroy_all_by_user(context.get_admin_context(), |
1323 | + uid) |
1324 | + with self.driver() as drv: |
1325 | + drv.delete_user(uid) |
1326 | + |
1327 | + def modify_user(self, user, access_key=None, secret_key=None, admin=None): |
1328 | + """Modify credentials for a user""" |
1329 | + uid = User.safe_id(user) |
1330 | + if access_key: |
1331 | + LOG.audit(_("Access Key change for user %s"), uid) |
1332 | + if secret_key: |
1333 | + LOG.audit(_("Secret Key change for user %s"), uid) |
1334 | + if admin is not None: |
1335 | + LOG.audit(_("Admin status set to %(admin)r" |
1336 | + " for user %(uid)s") % locals()) |
1337 | + with self.driver() as drv: |
1338 | + drv.modify_user(uid, access_key, secret_key, admin) |
1339 | + |
1340 | + def get_credentials(self, user, project=None, use_dmz=True): |
1341 | + """Get credential zip for user in project""" |
1342 | + if not isinstance(user, User): |
1343 | + user = self.get_user(user) |
1344 | + if project is None: |
1345 | + project = user.id |
1346 | + pid = Project.safe_id(project) |
1347 | + private_key, signed_cert = crypto.generate_x509_cert(user.id, pid) |
1348 | + |
1349 | + tmpdir = tempfile.mkdtemp() |
1350 | + zf = os.path.join(tmpdir, "temp.zip") |
1351 | + zippy = zipfile.ZipFile(zf, 'w') |
1352 | + if use_dmz and FLAGS.region_list: |
1353 | + regions = {} |
1354 | + for item in FLAGS.region_list: |
1355 | + region, _sep, region_host = item.partition("=") |
1356 | + regions[region] = region_host |
1357 | + else: |
1358 | + regions = {'nova': FLAGS.ec2_host} |
1359 | + for region, host in regions.iteritems(): |
1360 | + rc = self.__generate_rc(user, |
1361 | + pid, |
1362 | + use_dmz, |
1363 | + host) |
1364 | + zippy.writestr(FLAGS.credential_rc_file % region, rc) |
1365 | + |
1366 | + zippy.writestr(FLAGS.credential_key_file, private_key) |
1367 | + zippy.writestr(FLAGS.credential_cert_file, signed_cert) |
1368 | + |
1369 | + (vpn_ip, vpn_port) = self.get_project_vpn_data(project) |
1370 | + if vpn_ip: |
1371 | + configfile = open(FLAGS.vpn_client_template, "r") |
1372 | + s = string.Template(configfile.read()) |
1373 | + configfile.close() |
1374 | + config = s.substitute(keyfile=FLAGS.credential_key_file, |
1375 | + certfile=FLAGS.credential_cert_file, |
1376 | + ip=vpn_ip, |
1377 | + port=vpn_port) |
1378 | + zippy.writestr(FLAGS.credential_vpn_file, config) |
1379 | + else: |
1380 | + LOG.warn(_("No vpn data for project %s"), pid) |
1381 | + |
1382 | + zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(pid)) |
1383 | + zippy.close() |
1384 | + with open(zf, 'rb') as f: |
1385 | + read_buffer = f.read() |
1386 | + |
1387 | + shutil.rmtree(tmpdir) |
1388 | + return read_buffer |
1389 | + |
1390 | + def get_environment_rc(self, user, project=None, use_dmz=True): |
1391 | + """Get environment rc for user in project""" |
1392 | + if not isinstance(user, User): |
1393 | + user = self.get_user(user) |
1394 | + if project is None: |
1395 | + project = user.id |
1396 | + pid = Project.safe_id(project) |
1397 | + return self.__generate_rc(user, pid, use_dmz) |
1398 | + |
1399 | + @staticmethod |
1400 | + def __generate_rc(user, pid, use_dmz=True, host=None): |
1401 | + """Generate rc file for user""" |
1402 | + if use_dmz: |
1403 | + ec2_host = FLAGS.ec2_dmz_host |
1404 | + else: |
1405 | + ec2_host = FLAGS.ec2_host |
1406 | + # NOTE(vish): Always use the dmz since it is used from inside the |
1407 | + # instance |
1408 | + s3_host = FLAGS.s3_dmz |
1409 | + if host: |
1410 | + s3_host = host |
1411 | + ec2_host = host |
1412 | + rc = open(FLAGS.credentials_template).read() |
1413 | + # NOTE(vish): Deprecated auth uses an access key, no auth uses a |
1414 | + # the user_id in place of it. |
1415 | + if FLAGS.use_deprecated_auth: |
1416 | + access = user.access |
1417 | + else: |
1418 | + access = user.id |
1419 | + rc = rc % {'access': access, |
1420 | + 'project': pid, |
1421 | + 'secret': user.secret, |
1422 | + 'ec2': '%s://%s:%s%s' % (FLAGS.ec2_scheme, |
1423 | + ec2_host, |
1424 | + FLAGS.ec2_port, |
1425 | + FLAGS.ec2_path), |
1426 | + 's3': 'http://%s:%s' % (s3_host, FLAGS.s3_port), |
1427 | + 'os': '%s://%s:%s%s' % (FLAGS.osapi_scheme, |
1428 | + ec2_host, |
1429 | + FLAGS.osapi_port, |
1430 | + FLAGS.osapi_path), |
1431 | + 'user': user.name, |
1432 | + 'nova': FLAGS.ca_file, |
1433 | + 'cert': FLAGS.credential_cert_file, |
1434 | + 'key': FLAGS.credential_key_file} |
1435 | + return rc |
1436 | |
1437 | === modified file 'Authors' |
1438 | --- Authors 2011-10-04 09:43:55 +0000 |
1439 | +++ Authors 2011-10-27 13:05:25 +0000 |
1440 | @@ -1,5 +1,6 @@ |
1441 | Adam Gandelman <adamg@canonical.com> |
1442 | Adam Johnson <adjohn@gmail.com> |
1443 | +Ahmad Hassan <ahmad.hassan@hp.com> |
1444 | Alex Meade <alex.meade@rackspace.com> |
1445 | Alexander Sakhnov <asakhnov@mirantis.com> |
1446 | Andrey Brindeyev <abrindeyev@griddynamics.com> |
1447 | |
1448 | === modified file 'debian/changelog' |
1449 | --- debian/changelog 2011-10-12 14:33:25 +0000 |
1450 | +++ debian/changelog 2011-10-27 13:05:25 +0000 |
1451 | @@ -1,3 +1,33 @@ |
1452 | +nova (2011.3-0ubuntu6.3) UNRELEASED; urgency=low |
1453 | + |
1454 | + [ Scott Moser ] |
1455 | + * Removed db_pool complexities from nova.db.sqlalchemy.session (LP: #838581) |
1456 | + |
1457 | + [ Chuck Short ] |
1458 | + * debian/patches/fix-iscsi-target-path.patch: Fix ISCSI target path patch. |
1459 | + (LP: #871278) |
1460 | + * debian/control: Either install xen-hypervisor-4.1-amd64 or |
1461 | + xen-hypervisor-4.1-i386 for nova-compute-xen. (LP: #873243) |
1462 | + |
1463 | + [ Soren Hansen ] |
1464 | + * 2011.3-0ubuntu6.1 never made it past -proposed, due to 2011.3- |
1465 | + 0ubuntu6.2 which was a security fix (without the changes from |
1466 | + 2011.3-0ubuntu6.1). This upload brings the changes from 2011.3- |
1467 | + 0ubuntu6.1 back. |
1468 | + |
1469 | + -- Chuck Short <zulcss@ubuntu.com> Wed, 12 Oct 2011 14:33:25 -0400 |
1470 | + |
1471 | +nova (2011.3-0ubuntu6.2) oneiric-security; urgency=low |
1472 | + |
1473 | + * SECURITY UPDATE: fix information leak via invalid key |
1474 | + debina/patches/security-fix-lp868360.patch: adjust nova/auth/manager.py |
1475 | + to not return access, secret or admin fields for User error and |
1476 | + project_manager_id, description and member_ids for Project |
1477 | + - LP: #868360 |
1478 | + - CVE-2011-XXXX |
1479 | + |
1480 | + -- Jamie Strandboge <jamie@ubuntu.com> Tue, 25 Oct 2011 08:57:02 -0500 |
1481 | + |
1482 | nova (2011.3-0ubuntu6.1) oneiric-proposed; urgency=low |
1483 | |
1484 | [Scott Moser] |
1485 | |
1486 | === added file 'debian/patches/security-fix-lp868360.patch' |
1487 | --- debian/patches/security-fix-lp868360.patch 1970-01-01 00:00:00 +0000 |
1488 | +++ debian/patches/security-fix-lp868360.patch 2011-10-27 13:05:25 +0000 |
1489 | @@ -0,0 +1,70 @@ |
1490 | +From beee11edbfdd82cd81bc9c0fd75912c167892c2b Mon Sep 17 00:00:00 2001 |
1491 | +From: Ahmad Hassan <ahmad.hassan@hp.com> |
1492 | +Date: Mon, 1 Aug 2011 17:16:49 +0100 |
1493 | +Subject: [PATCH] Stop returning correct password on api calls |
1494 | + |
1495 | +Captured invalid signature exception in authentication step, so that |
1496 | +the problem is not returning exception to user, revealing the real |
1497 | +password. |
1498 | +Fixes bug 868360. |
1499 | + |
1500 | +Change-Id: Idb31f076a7b14309f0fda698261de816924da354 |
1501 | +--- |
1502 | + Authors | 1 + |
1503 | + nova/api/ec2/__init__.py | 3 ++- |
1504 | + nova/auth/manager.py | 10 ++-------- |
1505 | + 3 files changed, 5 insertions(+), 9 deletions(-) |
1506 | + |
1507 | +Index: nova-2011.3/Authors |
1508 | +=================================================================== |
1509 | +--- nova-2011.3.orig/Authors 2011-10-25 08:55:16.000000000 -0500 |
1510 | ++++ nova-2011.3/Authors 2011-10-25 08:56:17.000000000 -0500 |
1511 | +@@ -1,5 +1,6 @@ |
1512 | + Adam Gandelman <adamg@canonical.com> |
1513 | + Adam Johnson <adjohn@gmail.com> |
1514 | ++Ahmad Hassan <ahmad.hassan@hp.com> |
1515 | + Alex Meade <alex.meade@rackspace.com> |
1516 | + Alexander Sakhnov <asakhnov@mirantis.com> |
1517 | + Andrey Brindeyev <abrindeyev@griddynamics.com> |
1518 | +Index: nova-2011.3/nova/api/ec2/__init__.py |
1519 | +=================================================================== |
1520 | +--- nova-2011.3.orig/nova/api/ec2/__init__.py 2011-10-25 08:55:16.000000000 -0500 |
1521 | ++++ nova-2011.3/nova/api/ec2/__init__.py 2011-10-25 08:55:22.000000000 -0500 |
1522 | +@@ -188,7 +188,8 @@ |
1523 | + req.host, |
1524 | + req.path) |
1525 | + # Be explicit for what exceptions are 403, the rest bubble as 500 |
1526 | +- except (exception.NotFound, exception.NotAuthorized) as ex: |
1527 | ++ except (exception.NotFound, exception.NotAuthorized, |
1528 | ++ exception.InvalidSignature) as ex: |
1529 | + LOG.audit(_("Authentication Failure: %s"), unicode(ex)) |
1530 | + raise webob.exc.HTTPForbidden() |
1531 | + |
1532 | +Index: nova-2011.3/nova/auth/manager.py |
1533 | +=================================================================== |
1534 | +--- nova-2011.3.orig/nova/auth/manager.py 2011-10-25 08:55:16.000000000 -0500 |
1535 | ++++ nova-2011.3/nova/auth/manager.py 2011-10-25 08:55:22.000000000 -0500 |
1536 | +@@ -149,11 +149,7 @@ |
1537 | + return AuthManager().is_project_manager(self, project) |
1538 | + |
1539 | + def __repr__(self): |
1540 | +- return "User('%s', '%s', '%s', '%s', %s)" % (self.id, |
1541 | +- self.name, |
1542 | +- self.access, |
1543 | +- self.secret, |
1544 | +- self.admin) |
1545 | ++ return "User('%s', '%s')" % (self.id, self.name) |
1546 | + |
1547 | + |
1548 | + class Project(AuthBase): |
1549 | +@@ -200,9 +196,7 @@ |
1550 | + return AuthManager().get_credentials(user, self) |
1551 | + |
1552 | + def __repr__(self): |
1553 | +- return "Project('%s', '%s', '%s', '%s', %s)" % \ |
1554 | +- (self.id, self.name, self.project_manager_id, self.description, |
1555 | +- self.member_ids) |
1556 | ++ return "Project('%s', '%s')" % (self.id, self.name) |
1557 | + |
1558 | + |
1559 | + class AuthManager(object): |
1560 | |
1561 | === modified file 'debian/patches/series' |
1562 | --- debian/patches/series 2011-10-12 14:33:25 +0000 |
1563 | +++ debian/patches/series 2011-10-27 13:05:25 +0000 |
1564 | @@ -12,3 +12,4 @@ |
1565 | fix-lp863305-images-permission.patch |
1566 | fix-lp838581-removed-db_pool-complexities.patch |
1567 | fix-iscsi-target-path.patch |
1568 | +security-fix-lp868360.patch |
1569 | |
1570 | === modified file 'nova/api/ec2/__init__.py' |
1571 | --- nova/api/ec2/__init__.py 2011-09-22 09:33:49 +0000 |
1572 | +++ nova/api/ec2/__init__.py 2011-10-27 13:05:25 +0000 |
1573 | @@ -188,7 +188,8 @@ |
1574 | req.host, |
1575 | req.path) |
1576 | # Be explicit for what exceptions are 403, the rest bubble as 500 |
1577 | - except (exception.NotFound, exception.NotAuthorized) as ex: |
1578 | + except (exception.NotFound, exception.NotAuthorized, |
1579 | + exception.InvalidSignature) as ex: |
1580 | LOG.audit(_("Authentication Failure: %s"), unicode(ex)) |
1581 | raise webob.exc.HTTPForbidden() |
1582 | |
1583 | |
1584 | === modified file 'nova/auth/manager.py' |
1585 | --- nova/auth/manager.py 2011-08-26 13:31:14 +0000 |
1586 | +++ nova/auth/manager.py 2011-10-27 13:05:25 +0000 |
1587 | @@ -149,11 +149,7 @@ |
1588 | return AuthManager().is_project_manager(self, project) |
1589 | |
1590 | def __repr__(self): |
1591 | - return "User('%s', '%s', '%s', '%s', %s)" % (self.id, |
1592 | - self.name, |
1593 | - self.access, |
1594 | - self.secret, |
1595 | - self.admin) |
1596 | + return "User('%s', '%s')" % (self.id, self.name) |
1597 | |
1598 | |
1599 | class Project(AuthBase): |
1600 | @@ -200,9 +196,7 @@ |
1601 | return AuthManager().get_credentials(user, self) |
1602 | |
1603 | def __repr__(self): |
1604 | - return "Project('%s', '%s', '%s', '%s', %s)" % \ |
1605 | - (self.id, self.name, self.project_manager_id, self.description, |
1606 | - self.member_ids) |
1607 | + return "Project('%s', '%s')" % (self.id, self.name) |
1608 | |
1609 | |
1610 | class AuthManager(object): |
Hey Soren, did this package ever hit -proposed archive pocket? It's not clear to me why this is needed?