casbin-net / casbin-aspnetcore

Casbin.NET integration middleware and sample code for ASP.NET Core
https://github.com/casbin/Casbin.NET
Apache License 2.0
64 stars 20 forks source link

Enforcer lifecycle, caching etc. #34

Closed thoraj closed 2 years ago

thoraj commented 2 years ago

What is the intended lifecycle for enforcer? And how can we ensure enforcer use "fresh data" when:

Initially we tried using RedisWatcher approach, but it had limitations so we tried developing a different approach, also using Redis to notify processes to update casbin.

However we are not able to get this working (seem to be caching/remembering the old casbin_rules). So before digging more into this it would be really helpful if you could clarify:

  1. What should be the lifecycle scope for CasbinContext?
  2. What is the lifecycle scope of Enforcer if we use the DefaultEnforcerFactory

If both of these objects are made fresh for each request, changes to the database should be active immediately.

If the enforcer "lives" across requests, the enforcer.LoadPolicy() should still pull in changes from the database, but it appears db changes are not "honored" by the CasbinAuthorize attribute. We are not sure if the policies are in fact re-read from the database, and the .Enforce() decision has been cached, or if the policies themselves are cached, or if there is something else entirely going on.

Any help and insights are greatly appreciated.

casbin-bot commented 2 years ago

@sagilio @xcaptain @huazhikui

sagilio commented 2 years ago

Model and Enforcer have the default Scope lifecycle at the current version, you can change it by optional parameters, And in Casbin v2.0, we can use synced model, so the model can have a singleton lifecycle.: https://github.com/casbin-net/casbin-aspnetcore/blob/0cb68a5b5ebf20424d60978f467ace325fe34309/src/Casbin.AspNetCore/ServiceCollectionExtension.cs#L19-L37

If you use the default option, the changes to the database will be active immediately, Would you like to provide a sample for us to handle this problem

thoraj commented 2 years ago

I feel like I'm on a fairly steep learning curve here.

What I'm trying to achieve is:

  1. Use EFCoreAdapter with a PosgresSQL and a custom CasbinContext (VerjiCasbinContext)
  2. Access Enforcer in service components which are resolved from contiainer/ServiceCollection
  3. Use CasbinAuthorize attribute with custom RequestTransformers
  4. Use filtering to avoid loading all data
    • Filtering must be made from request parameter (i.e picking from HttpContext somehow)

I think the approach I have now is wrong and has issues with component lifecycles. Currently I'm registering Casbin components in an Autofac container. The CasbinAuthorize middleware resolve the Enforcer from the DefaultEnforcerFactory which resolves it from Autofac/ServiceCollection. In other components I inject Enforcer directly. I think this is causing issues with lifecycle management and concurrency.

So to elevate my understanding of Casbin, I would like to hear about the recommended ways to achieve the goals I described above.

I hope there is a better way than to re-implement most of the servicecollection registrations, and that overriding the correct dependencies will do the trick.

thoraj commented 2 years ago

We have come a bit further, so I'll just summarize what we've done in case there are comments, or in case this could be of benefit to others:

To leverage the most of the logic already in place we abandoned most of our custom DI registrations, and try to rely more on the casbin built in handling of scopes etc. So now we instead:

In VerjiEnforcerProvider we inject:

This lets us create an adapter, and an Enforcer, and lets us customize the adapter and enforcer to ensure:

With this approach things started to behave a lot better wrt. object lifecycle, and data staleness.