Компания-исполнитель, подразделение, подразделение Контроль доступа пользователей в MVC4 с EF

Это мой первый вопрос о stackoverflow, поэтому будьте осторожны. Я пишу клиентский портал для складского приложения с использованием MVC4, Entity Framework и SimpleMembership. На складе хранится содержимое нескольких компаний. В каждой компании есть подразделения и отделы. Пользователи будут иметь разный доступ к информации о своей компании, подразделениях и отделах. Ищу элегантное решение для контроля доступа. Пока моя модель выглядит так:

public class UserProfile
{
    UserProfile()
    {
        this.AccessControl = new HashSet<AccessControl>();
    }

    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string UserName { get; set; }
    public Nullable<int> CompanyId { get; set; }
    public virtual ICollection<AccessControl> { get; set; }
    public virtual Company Company { get; set; }
}

public class AccessControl
{
    public int AccessControlId { get; set; }
    public int UserId { get; set; }
    public int CompanyId { get; set; }
    public Nullable<int> DivisionId { get; set; }
    public Nullable<int> DepartmentId { get; set; }
    public Boolean ReadAccess { get; set; }
    public Boolean WriteAccess { get; set; }

    // other properties for access control

    public virtual UserProfile UserProfile { get; set; }
    public virtual Company Company { get; set; }
    public virtual Division Division { get; set; }
    public virtual Department Department { get; set; }
}

public class Content
{
    public int ContentId { get; set; }
    public int CompanyId { get; set; }
    public int DivisionId { get; set; }
    public int DepartmentId { get; set; }

    // Various other properties

    public virtual Company Company { get; set; }
    public virtual Division Division { get; set; }
    public virtual Department { get; set; }
}

Я думал, что NULL-разделение означает все подразделения, а NULL-подразделение означает все подразделения. Мои вопросы:

  1. Каков элегантный способ написать метод репозитория для получения списка объектов Content для пользователя на основе их списка управления доступом, а также для заполнения списков выбора подразделений и отделов в представлениях CRUD?
  2. Есть ли лучший способ смоделировать этот список управления доступом?

person tomc    schedule 02.03.2013    source источник
comment
У вас уже есть неэлегантное решение, которое вы могли бы улучшить? Возможные null случаи для компании, подразделения и отдела делают решение довольно сложным, даже более элегантным решением, которое не будет иметь длинной последовательности различий в регистрах. Без nulls запрос мог бы быть: var query = from c in context.Contents join a in context.AccessControls on new { c.CompanyId, c.DivisionId, c.DepartmentId } equals new { a.CompanyId, a.DivisionId, a.DepartmentId } where a.UserProfile.UserId == givenUserId select c; Но мне не удалось как-то расширить это, чтобы охватить nulls.   -  person Slauma    schedule 02.03.2013
comment
Что касается списков выбора вашего подразделения / отдела, можете ли вы подробнее рассказать о том, что вы планируете для представлений CRUD? (т.е. что вы хотите делать с обзором)   -  person scott-pascoe    schedule 02.03.2013
comment
Спасибо за ответ. Если у пользователя A есть два объекта контроля доступа. Один, который предоставляет доступ к Divison1, Бухгалтерия, а другой, который дает доступ к Division1, Treasury, тогда списки выбора при создании, обновлении или удалении будут обеспечивать Division 1 только для Division, а Account and Treasury в списке выбора для Department. Если у пользователя есть один объект управления доступом для всех подразделений (т. Е. Подразделение имеет значение NULL) и отдела продаж, то списки выбора будут содержать все определенные подразделения и только отдел продаж.   -  person tomc    schedule 03.03.2013
comment
Слаума, к сожалению, я очень долго боролся с решением. Одна мысль заключалась в том, чтобы запросить фильтрацию Content Dbset с каждым критерием объекта управления доступом и объединить результат до тех пор, пока не будет построена полная коллекция на основе доступа пользователя. Но я не знаю, как это сделать с итерацией по всем объектам управления доступом.   -  person tomc    schedule 03.03.2013


Ответы (2)


Я не думаю, что это решает все ваши вопросы, но я думаю, что репозиторий выглядит примерно так:

public class accessRepository
{
    accessContext context = new accessContext();

    public IQueryable<Content> GetAccessibleContentFor(int userId)
    {
        var up = context.UserProfiles.Single(u => u.UserId == userId);
        var companyId = up.CompanyId;

        return from c in context.Content 
               where c.CompanyId == companyId 
               && (up.AccessControl.Any(
                    a=> 
                        a.CompanyId == c.CompanyId && 
                        a.DivisionId == c.DivisionId && 
                        a.DepartmentId == c.DepartmentId) 
               || up.AccessControl.Any(
                    a=>a.CompanyId == c.CompanyId && 
                        a.DivisionId == c.DivisionId && 
                        a.DepartmentId == null)
               || up.AccessControl.Any(
                    a=>
                        a.CompanyId == c.CompanyId && 
                        a.DivisionId == null)
               select c;
    }
}

позволит вам вернуть доступный контент, если:

  1. Контент принадлежит Компании пользователя.
  2. Пользователь может получить доступ к контенту компании, подразделения и отдела
  3. Или пользователь может получить доступ к контенту компании и подразделения (всех отделов)
  4. Или пользователь может получить доступ к контенту компании (всех подразделений) [в данном случае предполагается, что все подразделения].
person scott-pascoe    schedule 02.03.2013
comment
Я сомневаюсь, что это сработает, потому что вы смешиваете LINQ-to-Entities с LINQ-to-Objects в одном запросе (a.XXX - значения в памяти, а c.XXX - значения базы данных), что, вероятно, вызовет исключение во время выполнения. - person Slauma; 02.03.2013
comment
Я не уверен, что ваш комментарий верен. Здесь все является IQueryable и, следовательно, должно иметь возможность генерировать SQL вплоть до фактического запроса. Я играл с созданием тестовой реализации, и пока похоже, что она будет работать. Конечно, предложение Дэвиса Броссара мне нравится даже больше, чем мое решение. - person scott-pascoe; 03.03.2013
comment
up.AccessControl не является IQueryable<T>, это просто локальная коллекция в памяти, заполненная отложенной загрузкой. Я ожидал бы печально известного исключения Unable to create constant value bla bla ... (stackoverflow.com/questions/7220867/ ). Но может я ошибаюсь ... - person Slauma; 04.03.2013

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

пользователь может получить доступ к контенту на складе, если content.department == user.department && content.company == user.company.

XACML звучит как идеальная модель. Я написал эту демонстрацию, в которой я контролирую доступ к заказам на покупку в зависимости от покупателя, суммы, местоположения и статуса заказа на покупку. Мне не нужно менять код приложения, потому что я использую XACML извне.

person David Brossard    schedule 03.03.2013