@f_ban

.Net. Как реализовать безопасность доступа к данным в модели?

Представим себе некую учетную систему компании, производящей сферических коней.
Естественно, в модели данных этой учетной системы есть модель данных SphericalHorse:
public class SphericalHorse
    {
        [Key]
        public Guid SerialNumber { get; set; }
        public float Radius { get; set; }
        public string Color { get; set; }
    }

Руководство компании решило, что серийный номер коня будет присваиваться в момент добавления информации о новом изделии в БД и реализация данного функционала будет отдана системе хранения. Далее это же руководство решило, что даст возможность пользователям УС с ролью ServiceManager менять серийный номер, только им и больше никому. В учетной системе используется система безопасности .Net на основе ролей.
В качестве инфраструктуры, связывающей модель данных и систему хранения может быть использована любая ORM, в частности для тестирования используется Entity Framework.
Отразим эти бизнес-требования (а они же и правила безопасности доступа к свойству SerialNumber) в тестах класса SphericalHorse.
[TestMethod]
        public void SphericalHorse_Constructor_Autogenerate_SerialNumber()
        {
            SphericalHorse horse = new SphericalHorse() { };

            using(SecureDALCtxt ctx = new SecureDALCtxt())
            {
                ctx.Horses.Add(horse);
                ctx.SaveChanges();
            }
            
            Assert.AreNotEqual(Guid.Empty, horse.SerialNumber);
        }

        [TestMethod]
        public void SphericalHorse_SetSerialNumber_NotServiceManager()
        {
            SphericalHorse horse;
            using(SecureDALCtxt ctx = new SecureDALCtxt())
            {
                horse = (from h in ctx.Horses select h).First();
            }

            Assert.IsNotNull(horse);

            try
            {
                Guid newSN = new Guid("ADD1098D-2EF4-4B64-8BC7-6BCAB08A9331");
                horse.SerialNumber = newSN;
            }
            catch (SecurityException)
            {
                return;
            }

            Assert.Fail("Ожидалось исключение безопасности");
        }

        [TestMethod]
        public void SphericalHorse_SetSerialNumber_ServiceManager()
        {
           

            SphericalHorse horse;
            using (SecureDALCtxt ctx = new SecureDALCtxt())
            {
                horse = (from h in ctx.Horses select h).First();
            }

            Assert.IsNotNull(horse);

            Thread.CurrentPrincipal = new GenericPrincipal(
               new GenericIdentity("Manager"),
               new string[] { "ServiceManager" }
               );

            Guid newSN = new Guid("A5CA5613-EAFB-41C1-8192-0FA5C79809D9");
            horse.SerialNumber = newSN;

            Assert.AreEqual(newSN, horse.SerialNumber);
        }

Реализуем бизнес правила в модели
public class SphericalHorse
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid SerialNumber {
            get;
            [PrincipalPermission(SecurityAction.Demand, Role = "ServiceManager")]
            set;
        }

        public float Radius { get; set; }

        public string Color { get; set; }
    }

В результате, ни один из тестов не прошел. Все падают в момент, когда Entity Framework пытается установить значение свойства SerialNumber.

Изменим немного интерфейс коня
public class SphericalHorse
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid SerialNumber {
            get;
            
            private set;
        }

        [PrincipalPermission(SecurityAction.Demand, Role = "ServiceManager")]
        public void SetSerialNumber(Guid value)
        {
            this.SerialNumber = value;
        }

        public float Radius { get; set; }

        public string Color { get; set; }
    }


Ну и соответственно тесты...
В итоге, все вроде работает в соответствии с бизнес-требованиями.
В тоже время есть два момента:
  1. Мы потеряли удобство использования методов-асессоров
  2. Если вдруг руководство компании в будущем решит изменить правила доступа к другим свойствам коня, то придется переписывать приложения, которые используют модель коня

Подскажите пожалуйста, как более правильно реализовывать такие задачи?
  • Вопрос задан
  • 136 просмотров
Решения вопроса 1
@f_ban Автор вопроса
Благодарю за ответ.
На самом деле, у меня проблема скорее не уровня реализации, а уровня архитектуры.
Почитав Фаулера, пришел к выводу, что не совсем туда, куда нужно попытался впихнуть бизнес-логику безопасности.

Нужно выделить слой служб и в этом слое уже реализовывать логику безопасности. А сам слой служб выставлять как програмный интерфейс бизнес-логики приложения.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
yarosroman
@yarosroman
C# the best
Конечно же EF будет выкидывает исключение, в MSSQL свойству с атрибутом DatabaseGenerated(DatabaseGeneratedOption.Identity) будет соответствовать IDENTITY поле, которое просто так нельзя поменять, выход один, создание триггера на INSERT, который будет генерировать уникальный номер, при этом без проблем будет UPDATE проходить.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы