.Net Core [Authorize] - Или вместо А для проверки прав доступа - PullRequest
0 голосов
/ 13 июня 2019

Следующий код авторизации фильтра проходит, если пользователь ОБА Role из "admin" AND a Policy из "Нью-Йорк". Как я могу изменить его на «админ» OR «Нью-Йорк»

[Authorize(Policy = "New York", Roles = "admin")]

В данном конкретном случае я хочу оператор OR, а не AND.

1 Ответ

1 голос
/ 13 июня 2019

Нет встроенных функций для этого.

Но вы можете легко реализовать пользовательский LogicalOrPolicyProvider (и также обработчик) для достижения той же цели. LogicalOrPolicyProvider будет создавать политику динамически в соответствии с именем политики, например:

[Authorize(Policy="Choice: policy='New York'| role= ADMIN")]

Приведенный выше атрибут создаст новую политику, которая должна соответствовать политике «Нью-Йорк» или требует роли ADMIN

Более того, мы могли бы определить некоторые правила для обработки более общих случаев. Допустим, хотим составить следующие требования:

Choice: policy='New York'| role= ADMIN
Choice: policy='New York'| role= 'ADMIN'
Choice: policy='New York'| policy = 'WC' | role= root | role = 'GVN'

Вы можете определять свои собственные правила так, как вам нравится, я лично предпочитаю:

  1. Начните с токена Choice, за которым следует разделитель : (может иметь несколько необязательных пробелов ' ')
  2. Политика определяется policy=policyName, если policyName содержит пробел, вы должны окружить его ''. Вы можете определить многие policy, как вам нравится
  3. Роль определяется role = roleName. Вы также можете определить столько ролей, сколько захотите.
  4. Все policy & role определения разделены |.

Реализация вышеуказанного дизайна:

Давайте определим LogicalOrRequirement для хранения всех возможных политик:

public class LogicalOrRequirement : IAuthorizationRequirement
{
    public IList<AuthorizationPolicy> Policies { get; }

    public LogicalOrRequirement(IList<AuthorizationPolicy> policies)
    {
        this.Policies = policies;
    }
}

Если какая-либо из этих политик будет успешной, просто пройдите мимо:

public class LogicalOrAuthorizationHandler : AuthorizationHandler<LogicalOrRequirement>
{

    public LogicalOrAuthorizationHandler(IHttpContextAccessor httpContextAccessor)
    {
        this._httpContextAccessor = httpContextAccessor;
    }

    private readonly IHttpContextAccessor _httpContextAccessor;

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, LogicalOrRequirement requirement)
    {
        var httpContext = this._httpContextAccessor.HttpContext;
        var policyEvaluator = httpContext.RequestServices.GetRequiredService<IPolicyEvaluator>();
        foreach (var policy in requirement.Policies)
        {
            var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, httpContext);
            if (authenticateResult.Succeeded)
            {
                context.Succeed(requirement);
            }
        }
    }
}

Теперь давайте создадим политику динамически с помощью PolicyProvider:

public class LogicalOrPolicyProvider : IAuthorizationPolicyProvider
{
    const string POLICY_PREFIX = "Choice";
    const string TOKEN_POLICY="policy";
    const string TOKEN_ROLE="role";
    public const string Format = "Choice: policy='p3' | policy='p2' | role='role1' | ..."; 

    private AuthorizationOptions _authZOpts { get; }
    public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }

    public LogicalOrPolicyProvider(IOptions<AuthorizationOptions> options )
    {
        _authZOpts = options.Value;
        FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
    }


    // Choice: policy= | policy= | role= | role = ...
    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        if (policyName.StartsWith(POLICY_PREFIX, StringComparison.OrdinalIgnoreCase))
        {   
            var policyNames = policyName.Substring(POLICY_PREFIX.Length);
            var startIndex = policyNames.IndexOf(":");
            if(startIndex == -1 || startIndex == policyNames.Length)
            {
                throw new ArgumentException($"invalid syntax, must contains a ':' before tokens. The correct format is {Format}");
            }
            // skip the ":" , and turn it into the following list
            //     [[policy,policyName],[policy,policName],...[role,roleName],...,]
            var list= policyNames.Substring(startIndex+1)
                .Split("|")
                .Select(p => p.Split("=").Select(e => e.Trim().Trim('\'')).ToArray() )
                ;

            // build policy for roleNames
            var rolesPolicyBuilder = new AuthorizationPolicyBuilder();
            var roleNames =list.Where(arr => arr[0].ToLower() == TOKEN_ROLE)
                .Select(arr => arr[1])
                .ToArray();
            var rolePolicy = rolesPolicyBuilder.RequireRole(roleNames).Build();

            // get policies with all related names
            var polices1= list.Where(arr => arr[0].ToLower() == TOKEN_POLICY);
            var polices=polices1 
                .Select(arr => arr[1])
                .Select(name => this._authZOpts.GetPolicy(name))  // if the policy with the name doesn exit => null
                .Where(p => p != null)                            // filter null policy
                .Append(rolePolicy)
                .ToList();

            var pb= new AuthorizationPolicyBuilder();
            pb.AddRequirements(new LogicalOrRequirement(polices));
            return Task.FromResult(pb.Build());
        }

        return FallbackPolicyProvider.GetPolicyAsync(policyName);
    }

    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        return FallbackPolicyProvider.GetDefaultPolicyAsync();
    }
}

Наконец, не забудьте зарегистрировать две службы в вашем Startup.cs:

services.AddSingleton<IAuthorizationPolicyProvider, LogicalOrPolicyProvider>();
services.AddSingleton<IAuthorizationHandler, LogicalOrAuthorizationHandler>();

Теперь, когда вы хотите логическую or композицию, просто добавьте [Authorize(Policy="Choice: ...")]:

[Authorize(Policy="Choice: policy='New York'| role= ADMIN")]
public IActionResult Privacy()
{
    return View();
}
...