E
E
EVG2017-01-07 02:18:33
.NET
EVG, 2017-01-07 02:18:33

.net. How to implement data access security in the model?

Imagine some kind of accounting system for a company that produces spherical horses.
Naturally, in the data model of this accounting system there is a SphericalHorse data model:

public class SphericalHorse
    {
        [Key]
        public Guid SerialNumber { get; set; }
        public float Radius { get; set; }
        public string Color { get; set; }
    }

The company's management decided that the horse's serial number would be assigned at the time of adding information about a new product to the database, and the implementation of this functionality would be given to the storage system. Further, the same management decided that it would allow US users with the ServiceManager role to change the serial number, only for them and no one else. The accounting system uses .Net role-based security.
Any ORM can be used as an infrastructure that connects the data model and the storage system, in particular, the Entity Framework is used for testing.
Let's reflect these business requirements (and they are also security rules for access to the SerialNumber property) in tests of the SphericalHorse class.
[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);
        }

Implementing business rules in the model
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; }
    }

As a result, none of the tests passed. Everyone crashes the moment the Entity Framework tries to set the value of the SerialNumber property.
Let's change the interface of the horse a bit
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; }
    }

Well, and, accordingly, tests ...
As a result, everything seems to work in accordance with business requirements.
At the same time, there are two things:
  1. We have lost the usability of accessor methods
  2. If suddenly the company's management decides in the future to change the rules for accessing other properties of the horse, then you will have to rewrite applications that use the horse model

Can you please tell me how to implement such tasks more correctly?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
E
EVG, 2017-01-10
@f_ban

Thank you for your reply.
In fact, my problem is not the implementation level, but the architecture level.
After reading Fowler, I came to the conclusion that not quite where I needed to try to shove the security business logic.
It is necessary to select a layer of services and implement the security logic in this layer. And the service layer itself should be exposed as a program interface for the business logic of the application.

R
Roman, 2017-01-07
@yarosroman

Of course, EF will throw an exception, in MSSQL the property with the DatabaseGenerated(DatabaseGeneratedOption.Identity) attribute will correspond to the IDENTITY field, which simply cannot be changed, there is only one way out, creating a trigger on INSERT that will generate a unique number, while UPDATE will pass without problems .

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question