On Aug 8, 2014, at 8:49 AM, Pendergrass, Eric <eric.pendergr...@hp.com> wrote:

> Hi,
>  
> We have been struggling to get a decorator working for proposed new RBAC 
> functionality in ceilometer-api.  We’re hitting a problem where GET request 
> query parameters are mucked up by our decorator.  Here’s an example call:
>  
> curl -H "X-Auth-Token:$TOKEN" 
> 'http://localhost:8777/v2/meters?q.field=project_id&q.value=8c678720fb5b4e3bb18dee222d7d7933'
>  
> And here’s the decorator method (we’ve tried changing the kwargs, args, etc. 
> with no luck):
>  
> _ENFORCER = None
>  
> def protected(controller_class):
>  
>     global _ENFORCER
>     if not _ENFORCER:
>         _ENFORCER = policy.Enforcer()
>  
>     def wrapper(f):
>         @functools.wraps(f)
>         def inner(self, **kwargs):
>             pdb.set_trace()
>             self._rbac_context = {}

You need to be careful saving request state on the controller. The controller 
may be shared by multiple requests (I see below that you’re creating a 
MeterController for each incoming request, but that won’t be the case for all 
controller types). It’s better to store the value in the pecan.request, which 
is a thread-safe request-specific object.

If you just need to store some values, and not test ACLs it in the decorator, 
you could use a Pecan hook [1] like we do with the configuration settings [2].

Doug

1 - http://pecan.readthedocs.org/en/latest/hooks.html
2 - 
http://git.openstack.org/cgit/openstack/ceilometer/tree/ceilometer/api/hooks.py#n27

>             if not _ENFORCER.enforce('context_is_admin',
>                                      {},
>                                      {'roles': 
> pecan.request.headers.get('X-Roles', "").split(",")}):
>                 self._rbac_context['project_id'] = 
> pecan.request.headers.get('X-Project-Id')
>                 self._rbac_context['user_id'] = 
> pecan.request.headers.get('X-User-Id')
>             return f(self, **kwargs)
>         return inner
>     return wrapper
>  
> tried this too:
>  
> _ENFORCER = None
>  
> def protected(*args):
>  
>     controller_class = 'meter'
>     global _ENFORCER
>     if not _ENFORCER:
>         _ENFORCER = policy.Enforcer()
>  
>     def wrapper(f, *args):
>         def inner(self, *args):
>             pdb.set_trace()
> #            self._rbac_context = {}
> #            if not _ENFORCER.enforce('context_is_admin',
> #                                     {},
> #                                     {'roles': 
> pecan.request.headers.get('X-Roles', "").split(",")}):
> #                self._rbac_context['project_id'] = 
> pecan.request.headers.get('X-Project-Id')
> #                self._rbac_context['user_id'] = 
> pecan.request.headers.get('X-User-Id')
>             #return f(*args)
>             f(self, *args)
>         return inner
>     return wrapper
>  
> and here’s how it’s used:
>  
> class MetersController(rest.RestController):
>     """Works on meters."""
>  
>     _rbac_context = {}
>     @pecan.expose()
>     def _lookup(self, meter_name, *remainder):
>         return MeterController(meter_name), remainder
>  
>     @wsme_pecan.wsexpose([Meter], [Query])
>     @rbac_validate.protected('meters')
>     def get_all(self, q=None):
>         """Return all known meters, based on the data recorded so far.
>  
>         :param q: Filter rules for the meters to be returned.
>         """
>         q = q or [] …
>  
>  
> but we get errors similar to below where the arg parser cannot find the query 
> parameter because the decorator doesn’t take a q argument as 
> MetersController.get_all does. 
>  
> Is there any way to get a decorator to work within the v2 API code and wsme 
> framework or should we consider another approach?  Decorators would really 
> simplify the RBAC idea we’re working on, which is mostly code-implemented 
> save for this fairly major problem.
>  
> I have a WIP registered BP on this at 
> https://blueprints.launchpad.net/ceilometer/+spec/ready-ceilometer-rbac-keystone-v3.
>  
> If I can provide more details I’ll be happy to.
>  
> Thanks
> Eric
>  
>   /usr/local/bin/ceilometer-api(10)<module>()
> -> sys.exit(api())
>   /opt/stack/ceilometer/ceilometer/cli.py(96)api()
> -> srv.serve_forever()
>   /usr/lib/python2.7/SocketServer.py(227)serve_forever()
> -> self._handle_request_noblock()
>   /usr/lib/python2.7/SocketServer.py(284)_handle_request_noblock()
> -> self.process_request(request, client_address)
>   /usr/lib/python2.7/SocketServer.py(310)process_request()
> -> self.finish_request(request, client_address)
>   /usr/lib/python2.7/SocketServer.py(323)finish_request()
> -> self.RequestHandlerClass(request, client_address, self)
>   /usr/lib/python2.7/SocketServer.py(638)__init__()
> -> self.handle()
>   /usr/lib/python2.7/wsgiref/simple_server.py(124)handle()
> -> handler.run(self.server.get_app())
>   /usr/lib/python2.7/wsgiref/handlers.py(85)run()
> -> self.result = application(self.environ, self.start_response)
>   
> /opt/stack/python-keystoneclient/keystoneclient/middleware/auth_token.py(663)__call__()
> -> return self.app(env, start_response)
>   /opt/stack/ceilometer/ceilometer/api/app.py(97)__call__()
> -> return self.v2(environ, start_response)
>   
> /usr/local/lib/python2.7/dist-packages/pecan/middleware/static.py(151)__call__()
> -> return self.app(environ, start_response)
>   
> /usr/local/lib/python2.7/dist-packages/pecan/middleware/debug.py(289)__call__()
> -> return self.app(environ, start_response)
>   
> /usr/local/lib/python2.7/dist-packages/pecan/middleware/recursive.py(56)__call__()
> -> return self.application(environ, start_response)
>   /opt/stack/ceilometer/ceilometer/api/middleware.py(83)__call__()
> -> app_iter = self.app(environ, replacement_start_response)
>   /usr/local/lib/python2.7/dist-packages/pecan/core.py(750)__call__()
> -> return super(Pecan, self).__call__(environ, start_response)
>   /usr/local/lib/python2.7/dist-packages/pecan/core.py(616)__call__()
> -> self.invoke_controller(controller, args, kwargs, state)
>   /usr/local/lib/python2.7/dist-packages/pecan/core.py(526)invoke_controller()
> -> result = controller(*args, **kwargs)
>   /usr/local/lib/python2.7/dist-packages/wsmeext/pecan.py(79)callfunction()
> -> pecan.request.body, pecan.request.content_type
>   /usr/local/lib/python2.7/dist-packages/wsme/rest/args.py(276)get_args()
> -> from_args = args_from_args(funcdef, args, kwargs)
> > /usr/local/lib/python2.7/dist-packages/wsme/rest/args.py(189)args_from_args()
> -> funcdef.get_arg(argname).datatype, value
>  
> _______________________________________________
> OpenStack-dev mailing list
> OpenStack-dev@lists.openstack.org
> http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev

_______________________________________________
OpenStack-dev mailing list
OpenStack-dev@lists.openstack.org
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev

Reply via email to