[openstack-dev] [Ceilometer] Way to get wrapped method's name/class using Pecan secure decorators?

Ryan Petrello ryan.petrello at dreamhost.com
Fri Aug 15 22:20:14 UTC 2014


Eric,                                                                                                                                     

Doug's correct - this looks like a bug in pecan that occurs when you subclass
both rest.RestController and hooks.HookController.  I'm working on a bug fix as
we speak.  In the meantime, have you tried applying hooks at a global
application level?  This approach should still work.

On 08/14/14 04:38 PM, Pendergrass, Eric wrote:
> Sure, Doug.  We want the ability to selectively apply policies to certain
> Ceilometer
> API methods based on user/tenant roles.
> 
> For example, we want to restrict the ability to execute Alarm deletes to
> admins and user/tenants who have a special role, say "domainadmin".
> 
> The policy file might look like this:
> {
>     "context_is_admin":  [["role:admin"]],
>     "admin_and_matching_project_domain_id":  [["role:domainadmin"]],
>     "admin_or_cloud_admin": [["rule:context_is_admin"],
> ["rule:admin_and_matching_project_domain_id"]],
>     "telemetry:delete_alarms":  [["rule:admin_or_cloud_admin"]]
> }
> 
> The current acl.py and _query_to_kwargs access control setup either sets
> project_id scope to None (do everything) or to the project_id in the request
> header 'X-Project-Id'.  This allows for admin or project scope, but nothing
> in
> between.
> 
> We tried hooks.  Unfortunately we can't seem to turn the API controllers
> into
> HookControllers just by adding HookController to the Controller class
> definition.  It causes infinite recursion on API startup.  For example, this
> doesn't work because ceilometer-api will not start with it:
>     class MetersController(rest.RestController, HookController):
> 
> If there was a way to use hooks with the v2. API controllers that might work
> really well.
> 
> So we are left using the @secure decorator and deriving the method name from
> the request environ PATH_INFO and REQUEST_METHOD values.  This is how we
> determine the wrapped method within the class (REQUEST_METHOD + PATH_INFO =
> "telemetry:delete_alarms" with some munging).  We need the method name in
> order to
> selectively apply acces control to certain methods.
> 
> Deriving the method this way isn't ideal but it's the only thing we've
> gotten working 
> between hooks, @secure, and regular decorators.
> 
> I submitted a WIP BP here: https://review.openstack.org/#/c/112137/3.  It is
> slightly out of date but should give you a beter idea of our goals.
> 
> Thanks
> 
> > Eric,
> >
> > If you can give us some more information about your end goal, independent
> of the implementation, maybe we can propose an alternate technique to
> achieve the same thing.
> >
> > Doug
> >
> > On Aug 12, 2014, at 6:21 PM, Ryan Petrello <ryan.petrello at dreamhost.com>
> wrote:
> >
> > > Yep, you're right, this doesn't seem to work.  The issue is that
> > > security is enforced at routing time (while the controller is still
> > > actually being discovered).  In order to do this sort of thing with
> > > the `check_permissions`, we'd probably need to add a feature to pecan.
> > >
> > > On 08/12/14 06:38 PM, Pendergrass, Eric wrote:
> > >> Sure, here's the decorated method from v2.py:
> > >>
> > >>    class MetersController(rest.RestController):
> > >>        """Works on meters."""
> > >>
> > >>        @pecan.expose()
> > >>        def _lookup(self, meter_name, *remainder):
> > >>            return MeterController(meter_name), remainder
> > >>
> > >>        @wsme_pecan.wsexpose([Meter], [Query])
> > >>        @secure(RBACController.check_permissions)
> > >>        def get_all(self, q=None):
> > >>
> > >> and here's the decorator called by the secure tag:
> > >>
> > >>    class RBACController(object):
> > >>        global _ENFORCER
> > >>        if not _ENFORCER:
> > >>            _ENFORCER = policy.Enforcer()
> > >>
> > >>
> > >>        @classmethod
> > >>        def check_permissions(cls):
> > >>            # do some stuff
> > >>
> > >> In check_permissions I'd like to know the class and method with the
> @secure tag that caused check_permissions to be invoked.  In this case, that
> would be MetersController.get_all.
> > >>
> > >> Thanks
> > >>
> > >>
> > >>> Can you share some code?  What do you mean by, "is there a way for the
> decorator code to know it was called by MetersController.get_all"
> > >>>
> > >>> On 08/12/14 04:46 PM, Pendergrass, Eric wrote:
> > >>>> Thanks Ryan, but for some reason the controller attribute is None:
> > >>>>
> > >>>> (Pdb) from pecan.core import state
> > >>>> (Pdb) state.__dict__
> > >>>> {'hooks': [<ceilometer.api.hooks.ConfigHook object at 0x31894d0>,
> > >>>> <ceilometer.api.hooks.DBHook object at 0x3189650>,
> > >>>> <ceilometer.api.hooks.PipelineHook object at 0x39871d0>,
> > >>>> <ceilometer.api.hooks.TranslationHook object at 0x3aa5510>], 'app':
> > >>>> <pecan.core.Pecan object at 0x2e76390>, 'request': <Request at
> > >>>> 0x3ed7390 GET http://localhost:8777/v2/meters>, 'controller': None,
> > >>>> 'response': <Response at 0x3ed74d0 200 OK>}
> > >>>>
> > >>>>> -----Original Message-----
> > >>>>> From: Ryan Petrello [mailto:ryan.petrello at dreamhost.com]
> > >>>>> Sent: Tuesday, August 12, 2014 10:34 AM
> > >>>>> To: OpenStack Development Mailing List (not for usage questions)
> > >>>>> Subject: Re: [openstack-dev] [Ceilometer] Way to get wrapped
> method's name/class using Pecan secure decorators?
> > >>>>>
> > >>>>> This should give you what you need:
> > >>>>>
> > >>>>> from pecan.core import state
> > >>>>> state.controller
> > >>>>>
> > >>>>> On 08/12/14 04:08 PM, Pendergrass, Eric wrote:
> > >>>>>> Hi, I'm trying to use the built in secure decorator in Pecan for
> access control, and I'ld like to get the name of the method that is wrapped
> from within the decorator.
> > >>>>>>
> > >>>>>> For instance, if I'm wrapping MetersController.get_all with an
> @secure decorator, is there a way for the decorator code to know it was
> called by MetersController.get_all?
> > >>>>>>
> > >>>>>> I don't see any global objects that provide this information.  I
> can get the endpoint, v2/meters, with pecan.request.path, but that's not as
> elegant.
> > >>>>>>
> > >>>>>> Is there a way to derive the caller or otherwise pass this
> information to the decorator?
> > >>>>>>
> > >>>>>> Thanks
> > >>>>>> Eric Pendergrass
> > >>>>>
> > >>>>>> _______________________________________________
> > >>>>>> OpenStack-dev mailing list
> > >>>>>> OpenStack-dev at lists.openstack.org
> > >>>>>> http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
> > >>>>>
> > >>>>>
> > >>>>> --
> > >>>>> Ryan Petrello
> > >>>>> Senior Developer, DreamHost
> > >>>>> ryan.petrello at dreamhost.com
> > >>
> > >> _______________________________________________
> > >> OpenStack-dev mailing list
> > >> OpenStack-dev at lists.openstack.org
> > >> http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
> > >
> > > --
> > > Ryan Petrello
> > > Senior Developer, DreamHost
> > > ryan.petrello at dreamhost.com
> > >
> > > _______________________________________________
> > > OpenStack-dev mailing list
> > > OpenStack-dev at lists.openstack.org
> > > http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
> >
> >
> > _______________________________________________
> > OpenStack-dev mailing list
> > OpenStack-dev at lists.openstack.org
> > http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev



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


-- 
Ryan Petrello
Senior Developer, DreamHost
ryan.petrello at dreamhost.com



More information about the OpenStack-dev mailing list