Как разрешить общие интерфейсы с помощью Autofac

#c# #generics #asp.net-mvc-5 #inversion-of-control #autofac

#c# #общие #asp.net-mvc-5 #инверсия управления #autofac

Вопрос:

Используя Autofac в качестве контейнера DI, я столкнулся с проблемой при реализации общего обработчика разрешений для проверки, имеет ли пользователь / участник доступ к определенному объекту.

Службы разрешений используют следующий общий интерфейс:

 public interface IClaimsPrincipalPermission<in TPrincipal, in TAction, in TResource> 
    where TPrincipal : AppUserPrincipal
    where TAction : BaseSecurity.Actions
    where TResource : BaseSecurity.Resources
{
    IdentityResult CheckAccess(SecurityAction secuirtyAction, TPrincipal principal, TAction action, TResource resource);
    IdentityResult CheckAccess(SecurityAction secuirtyAction, TPrincipal principal, TAction action, TResource resource, string id);
}
 

Где примером реализации может быть:

 internal class FruitClaimsPrincipalPermission
    : IClaimsPrincipalPermission<AppUserPrincipal, UpdateAction, FruitResource>

{
    private readonly IFruitRepository _fruitRepository;

    public FruitClaimsPrincipalPermission(IFruitRepository fruitRepository)
    {
        if (fruitRepository == null)
            throw new ArgumentNullException("fruitRepository");
        _fruitRepository = fruitRepository;
    }

    #region Implementation of IClaimsPrincipalPermission<AppUserPrincipal, UpdateAction, FruitResource>
    public IdentityResult CheckAccess(System.Security.Permissions.SecurityAction secuirtyAction, AppUserPrincipal principal, CreateAction action, FruitResource resource) {
        if (principal == null || action == null || resource == null)
            throw new ArgumentNullException();

        if (!principal.Identity.IsAuthenticated)
            return IdentityResult.Failed(SecurityResouces.Requires_Authentication);

        if (!principal.IsSetupAdministrator)
            return IdentityResult.Failed(SecurityResouces.Requires_Setup_Admin_Role);

        return IdentityResult.Success;
    }

    public IdentityResult CheckAccess(System.Security.Permissions.SecurityAction secuirtyAction, AppUserPrincipal principal, CreateAction action, FruitResource resource, string id) {
        if (principal == null || action == null || resource == null)
            throw new ArgumentNullException();

        if (!principal.Identity.IsAuthenticated)
            return IdentityResult.Failed(SecurityResouces.Requires_Authentication);

        if (!principal.IsSetupAdministrator)
            return IdentityResult.Failed(SecurityResouces.Requires_Setup_Admin_Role);

        var fruit = _fruitRepository.GetById(id);

        if (fruit.AccountId != principal.AccountId)
            return IdentityResult.Failed(SecurityResouces.Owner_Account_User_Mismatch);

        return IdentityResult.Success;
    }
    #endregion
}
 

Веб-приложение находится в MVC 5 и .Net 4.5. Я создал пользовательский атрибут авторизации, который будет украшать любой контроллер, требующий авторизации доступа к определенному объекту. ResourceType и ActionType — это оба перечисления, которые представляют ресурс и действие в запросе авторизации.

 public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
    private readonly ResourceType[] _resources;
    private readonly ActionType _action;

    public ClaimsAuthorizeAttribute(ActionType action, params ResourceType[] resources)
    {
        _resources = resources;
        _action = action;
    }

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
    {
        var principal = new AppUserPrincipal(httpContext.User as ClaimsPrincipal);
        var action = ActionRepository.FindActions(_action);
        var resources = _resources.Select(ResourceRepository.FindResource).ToArray();
        return ClaimsAuthorization.CheckAccess(action, resources.First(), principal, "", out result);
    }

    public override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
    }

}
 

ClaimsAuthorization является подклассом ClaimsAuthorizationManager и реализует статический метод checkAccess .

 public class ClaimsAuthorization : ClaimsAuthorizationManager
{

    public static bool CheckAccess(Actions action, Resources resource, AppUserPrincipal principal, string entityId, out IdentityResult result)
    {
        result = DependencyResolver.Current.GetService<IPermissionExecutor>().CheckAccess(SecurityAction.Demand, principal, action, resource);
        return result.Succeeded;
    }
}
 

Проблема в том, что я не могу понять, как вызвать правильную реализацию IClaimsPrincipalPermission из статического метода checkAccess в классе ClaimsAuthorization .

Я не смог понять, как внедрить правильный экземпляр IClaimsPrincipalPermission в атрибут ClaimsAuthorizeAttribute (исследования заставили меня поверить, что это невозможно) из контроллера, и казалось, что моим единственным вариантом было разрешить зависимость из класса ClaimsAthorization . Поэтому я попытался разрешить зависимость с помощью DependencyResolver.Текущий.GetService и создание PermissionExecutor, который реализовал IPermissionExecutor. Чтобы больше не расширять этот вопрос, я не буду включать их здесь. Каков правильный подход к решению этой проблемы?