InvalidOperationException: политика не найдена - PullRequest
0 голосов
/ 04 декабря 2018

Я создаю веб-сайт в ядре dotnet и недавно начал использовать аутентификацию и авторизацию на основе утверждений.

В компоненте просмотра я проверяю, есть ли у пользователя доступ к политике.

public NavigationViewComponent(
    IContextManager contextManager,
    IAuthorizationService authorizationService)
{
    _contextManager = contextManager;
    _authorizationService = authorizationService;
}

public async Task<IViewComponentResult> InvokeAsync()
{
    var showAdmin = _contextManager.Principal != null &&
        (await _authorizationService.AuthorizeAsync(_contextManager.Principal, "Admin")).Succeeded;

    var vm = new NavigationViewModel
    {
        ShowAdmin = showAdmin
    };

    return View(vm);
}

Однако я получаю исключение InvalidOperationException: No policy found: Admin..

My startup.cs содержит следующее внутри метода ConfigureServices:

services.AddAuthorization(options =>
{
    options.AddPolicy("Admin",
        policy => policy.Requirements.Add(new HasPermissionRequirement("ADMIN")));
});

Что еще нужно настроитьчтобы заставить это работать правильно?

Для справки я регистрирую 3 дополнительных IAuthorizationHandler реализации и 1 IAuthorizationPolicyProvider.

Редактировать

Для справки, весь startup.cs выглядит примерно так .

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
    {
        // Name and policy settings
        options.Cookie.Name         = "account";
        options.Cookie.SameSite     = SameSiteMode.Strict;
        options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
        options.Cookie.HttpOnly     = true;

        // Sign the user out after 28 days of inactivity
        options.SlidingExpiration = true;
        options.ExpireTimeSpan    = TimeSpan.FromDays(28);

        // Challenge actions
        options.LoginPath          = new PathString("/account/login");
        options.ReturnUrlParameter = "returnUrl";
    });

    services.AddAuthorization(options =>
    {
        options.AddPolicy("Admin",
            policy => policy.Requirements.Add(new HasPermissionRequirement("ADMIN")));
    });

    services.AddSingleton<IAuthorizationHandler, HasPermissionHandler>();
    services.AddSingleton<IAuthorizationHandler, StrategyAuthorizationCrudHandler>();
    services.AddSingleton<IAuthorizationHandler, UserAuthorizationCrudHandler>();
    services.AddSingleton<IAuthorizationPolicyProvider, HasPermissionPolicyProvider>();

    // AddAntiforgery, AddSession,AddDistributedRedisCache and AddDataProtection omitted

    services.AddMvc()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
        .AddJsonOptions(options =>
        {
            options.SerializerSettings.ReferenceLoopHandling =
                ReferenceLoopHandling.Ignore;
        });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }

    app.UseCookiePolicy(new CookiePolicyOptions
    {
        CheckConsentNeeded    = httpContext => false,
        MinimumSameSitePolicy = SameSiteMode.Strict,
        Secure                = CookieSecurePolicy.SameAsRequest
    });

    app.UseAuthentication();

    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    });

    app.UseStaticFiles();

    var supportedCultures = new[]
    {
        new CultureInfo("en-GB")
    };

    app.UseRequestLocalization(new RequestLocalizationOptions
    {
        DefaultRequestCulture = new RequestCulture(supportedCultures[0]),
        SupportedCultures     = supportedCultures,
        SupportedUICultures   = supportedCultures
    });

    app.UseSession();

    app.UseMiddleware<UserMiddleware>();
    app.UseMiddleware<LoggingMiddleware>();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            "default",
            "{controller=Home}/{action=Index}/{id?}");
    });
}

HasPermissionRequirement.cs

public class HasPermissionRequirement : IAuthorizationRequirement
{
    public string Permission { get; private set; }

    public HasPermissionRequirement(string permission)
    {
        Permission = permission;
    }
}

HasPermissionHandler.cs

public class HasPermissionHandler : AuthorizationHandler<HasPermissionRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        HasPermissionRequirement requirement)
    {
        var hasPermission = context.User.HasClaim("Permission", requirement.Permission);
        if (hasPermission)
            context.Succeed(requirement);

        return Task.CompletedTask;
    }
}

HasPermissionPolicyProvider.cs

public class HasPermissionPolicyProvider : IAuthorizationPolicyProvider
{
    private const string PolicyPrefix = "HasPermission";

    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        if (!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
            return Task.FromResult<AuthorizationPolicy>(null);

        var permission = policyName.Substring(PolicyPrefix.Length);

        var policy = new AuthorizationPolicyBuilder();
        policy.AddRequirements(new HasPermissionRequirement(permission));

        return Task.FromResult(policy.Build());
    }

    public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => 
        Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
}

1 Ответ

0 голосов
/ 04 декабря 2018

Мне удалось решить эту проблему путем более тщательного изучения документации Microsoft после наведения в правильном направлении.

https://github.com/aspnet/Docs/blob/master/aspnetcore/security/authorization/iauthorizationpolicyprovider.md#multiple-authorization-policy-providers

При использовании пользовательских реализаций IAuthorizationPolicyProviderимейте в виду, что ASP.NET Core использует только один экземпляр IAuthorizationPolicyProvider.Если пользовательский поставщик не может предоставить политики авторизации для всех имен политик, он должен обратиться к поставщику резервных копий.

В результате я изменил реализацию своей HasPermissionPolicyProvider на CustomPolicyProvider и содержание ниже:

public class CustomPolicyProvider : DefaultAuthorizationPolicyProvider
{
    private const string PermissionPolicyPrefix = "HasPermission";

    public CustomPolicyProvider(IOptions<AuthorizationOptions> options) : base(options)
    {
    }

    public override async Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        var policy = await base.GetPolicyAsync(policyName);

        if (policy != null) return policy;

        if (policyName.StartsWith(PermissionPolicyPrefix, StringComparison.OrdinalIgnoreCase))
        {
            var permission = policyName.Substring(PermissionPolicyPrefix.Length);

            return new AuthorizationPolicyBuilder()
                .AddRequirements(new HasPermissionRequirement(permission))
                .Build();
        }

        return null;
    }
}

Это означает, что у вас может быть только один PolicyProvider, который должен обрабатывать всю вашу логику.Это изменение должно гарантировать, что он вызывает реализацию по умолчанию, если вам требуется логика нескольких обработчиков.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...