Closed VerdonTrigance closed 2 years ago
@sagilio @xcaptain @huazhikui
@sagilio
Oh, I was looking in a wrong place. I found this:
public static IServiceCollection AddCasbinAuthorizationCore(
this IServiceCollection services,
Action<CasbinAuthorizationOptions>? configureOptions = default,
ServiceLifetime defaultEnforcerProviderLifeTime = ServiceLifetime.Scoped,
ServiceLifetime defaultModelProviderLifeTime = ServiceLifetime.Scoped)
{
services.AddCasbin(configureOptions, defaultEnforcerProviderLifeTime, defaultModelProviderLifeTime);
services.TryAddSingleton<ICasbinAuthorizationContextFactory, DefaultCasbinAuthorizationContextFactory>();
services.TryAddScoped<IEnforceService, DefaultEnforcerService>();
services.TryAddSingleton<IRequestTransformersCache, RequestTransformersCache>();
// Can not change to TryAdd, because there interface may need more than one implement.
services.AddScoped<IAuthorizationHandler, CasbinAuthorizationHandler>();
services.AddSingleton<IRequestTransformer, BasicRequestTransformer>();
services.AddSingleton<IRequestTransformer, RbacRequestTransformer>();
services.AddSingleton<IRequestTransformer, KeyMatchRequestTransformer>();
services.AddAuthorizationCore();
return services;
}
and was trying to do this:
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> GetAll([FromQuery] int? pageNumber, [FromServices] IEnforceService enforceService
, [FromServices] ICasbinAuthorizationContext casbinCtx)
{
var currentSubject = casbinCtx.AuthorizationData.First().Value1;
var availableObjects = _casbinDbCtx.CasbinRules
.Where(p => p.V0 == currentSubject)
.Select(p => p.V1);
// other stuff
}
But application says "ICasbinAuthorizationContext not registered" and it's actually not. As we can see there is ICasbinAuthorizationContextFactory registering, not ICasbinAuthorizationContext.
As workaround I might use HttpContext and look for claim that was defined like identifier here in the options:
options.PreferSubClaimType = OpenIDStandardClaims.PreferredUsername;
But it's not best way I guess.
You can inject IEnforcerProvider
here, and use GetEnforcer
to get the instance.
If you want to get the current user, you can only use the User
property in the controller.
I think It is better to provide an extension method to get the correct sub
value.
Injecting IEnforcerProvider
works, but when it registered? I didn't find it neither in AddCasbinAuthorization()
nor AddCasbinAuthorizationCore()
.
I ended with this approach:
public MyController(ILogger<MyController> logger, MyDbCtx ctx, IConfiguration configuration,
CasbinDbContext casbinDbCtx, IOptions<CasbinAuthorizationOptions> casbinOptions)
{
_logger = logger;
_dbContext = ctx;
Configuration = configuration;
_casbinDbCtx = casbinDbCtx;
_casbinOptions = casbinOptions?.Value;
}
public async Task<IActionResult> GetAll([FromServices] IEnforcerProvider enforcerProvider)
{
var currentSubject = User.FindFirst(_casbinOptions?.PreferSubClaimType)?.Value;
var enforcer = enforcerProvider.GetEnforcer();
var perms = enforcer.GetPermissionsForUser(currentSubject);
// other stuff
}
Khm... I've got exception "{"Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.\r\nObject name: 'CasbinDbContext'."}"
when trying to AddPolicy here in my controller method:
public async Task<IActionResult> CreateObject([FromBody] NewObjectRequest data, [FromServices] IEnforcerProvider enforcerProvider)
{
// other stuff
var enforcer = enforcerProvider.GetEnforcer();
bool success = enforcer.AddPermissionForUser(currentSubject, actionPath, HttpMethods.Get.ToUpper());
}
My Casbin registration is:
public static void AddAclAuthorization(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<CasbinDbContext>(options => options.UseNpgsql(
configuration.GetConnectionString("Casbin"),
options => options.MigrationsHistoryTable("__CasbinMigrationsHistory")
.MigrationsAssembly("Org.Proj.Casbin")));
services.AddCasbinAuthorization(options =>
{
options.DefaultModelPath = Path.Combine("Authorization", "Models", configuration["Authorization:Casbin:ModelName"]);
options.DefaultEnforcerFactory = (p, m) =>
new Enforcer(m, new EFCoreAdapter(p.GetRequiredService<CasbinDbContext>()));
options.DefaultAuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme;
options.PreferSubClaimType = KeycloakCustomClaims.Email2;
options.AllowAnyone = true;
});
}
Why it's disposed? Lifetime of any DbContext is Transient. But lifetime of IEnforcerProvider is Scope. I even tried to change lifetime of my CasbinDbContext to Scope like this:
services.AddDbContext<CasbinDbContext>(options => options.UseNpgsql(
configuration.GetConnectionString("Casbin"),
options => options.MigrationsHistoryTable("__CasbinMigrationsHistory")
.MigrationsAssembly("Org.Proj.Casbin")));
ServiceLifetime.Scoped);
But nothing changed. So where it's been disposing?
As workaround I may get CasbinDbContext and manually create CasbinRule object. But it's not good.
@sagilio
Hi guys.
I have this at startup:
And looking for a way to retrieve all available business objects for current user in a ApiController. For doing this I assume to call NetCasbin.Enforcer.GetPermissionsForUser() in my controller, filter 'this and that' and return result to requestor. Actually I didn't find AddCasbinAuthorization() method in the repo and now looking for a way to get Enforcer or something else to make that call and adding DI to ApiController constructor:
I'm new to Casbin and not sure what to inject?