MVC3 / 4 Security and Model Binding for a Dynamic Object

I really need a second set of eyes, so I hope some of you can give me some feedback, I think I looked at it for too long.

I am trying to customize a website using ASP.NET MVC3, and on this site I need the flexibility to create dynamic objects. But I mean that in my database there are a number of tables that store information about the structure and the data contained in these dynamic objects. I work with an existing database, so I am limited (to a certain extent) by what I can change. When I query the database for a dynamic object (not a .NET 4.0 dynamic object), I pass in my identifier, and what I get is a simple object, maybe a few properties for internal use only, and a property that represents a set containing all the properties for my dynamic object. Therefore, if my dynamic object was for a person with a name, DoB and Sex, my collection will have three objects,one for each property. This allows the site administrator to add new fields at runtime, and the website will automatically display them, allow updates, etc. Now I have a model binding, currently working for both mapping and postback for this data structure. For each object in the collection, I visualize two pieces of data, a unique property identifier (which is currently a hidden field, and Id is a landmark) and the property value. My problem is the security aspect.and for postback for this data structure. For each object in the collection, I visualize two pieces of data, a unique property identifier (which is currently a hidden field, and Id is a landmark) and the property value. My problem is the security aspect.and for postback for this data structure. For each object in the collection, I visualize two pieces of data, a unique property identifier (which is currently a hidden field, and Id is a landmark) and the property value. My problem is the security aspect.

If I were dealing with strongly typed objects, I could create custom ViewModels and execute with it or add Bind () attributes to the action signature, but since the properties of these objects are flexible collections, I’m not sure how to approach it. the steps are simple enough, I can create a custom Authorize attribute and query the database for permissions, but I need to be able to restrict the behavior of collections to display and accept information based on user rights. For example, if I need to add a social security number property to a person’s object, I don’t want it to appear on the screen for certain people. But since a property is something that can change at runtime, so are permissions.

That's where I am as far as my thoughts go ...

, , , . , , ViewModel DisplayTemplate, , . - ModelBinder, Html.Display() Html.Editor() ModelBinder.

postbacks. , , Guid . , , , , , . , MetaData, , , , , , , , , IsValid, , .

MetaData Html.Display() Html.Editor() , , , .

, MVC, , ModelBinders ModelMetaDataProviders ModelValidationProviders.

, , , , , , , Google .

EDIT: . ,

EDIT: . ModelMetadataProvider.

public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
    {
        ModelMetadata metadata;
        if (containerType == typeof(PseudoObjectAttributeViewModel))
        {
            switch (propertyName)
            {
                case "StringValue":
                    metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(string), propertyName);
                    break;
                case "DateValue":
                    metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(DateTime?), propertyName);
                    break;
                case "DoubleValue":
                    metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(double?), propertyName);
                    break;
                case "LongValue":
                    metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(long?), propertyName);
                    break;
                case "BooleanValue":
                    metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(bool?), propertyName);
                    break;
                case "GuidValue":
                    metadata = new ModelMetadata(this, typeof(PseudoObjectAttribute), modelAccessor, typeof(Guid?), propertyName);
                    break;
                default:
                    return defaultMetadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
                    break;
            }
            DataAnnotationsModelMetadata daMetadata = (DataAnnotationsModelMetadata)metadata;

            System.Reflection.FieldInfo container = modelAccessor.Target.GetType().GetField("vdi");
            AddSupplimentalMetadata(daMetadata, (PseudoObjectAttributeViewModel)((System.Web.Mvc.ViewDataInfo)container.GetValue(modelAccessor.Target)).Container);
        }
        else
            metadata =  defaultMetadataProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
        return metadata;
    }

, GetMetadataForType(), .NET , , . ( teplate , , , , ).

Html.Editor(Model.PseudoObjectStructure.PseudoObjectControl.DataType)

, , , .

switch, . , MVC2 GetMetadataForProperty() propertyName, Func<object>, , MVC . , . , , , Reflection. , , . , , , - , AddSupplimentalMetadata(), DataAnnotationsModelMetadata, MVC .

. , , LINQ - nautre. - Display/EditorTemplate. , , .

+3
2

, , , , , , , . . .

- , , : -)

, , . , . , , , . /. - , , , . , , . , MVC.

. MVC4, MVC3. nHibernate

POCO

public class PseudoObject
{
    // Other properties and such...
    public virtual IList<PseudoObjectAttribute> Attributes { get; set; }
    // Other methods, etc...
}

public class PseudoObjectAttribute
{
    // Other properties and such...
    public virtual string Value { get; set; }

    //This holds all of the info I need for determine metadata & validation
    public virtual PseudoObjectStructure Structure { get; set; }
    // Other methods, etc...
}

public class PseudoObjectStructure
{ 
    public virtual bool IsRequired { get; set; }
    public virtual string RegularExpression { get; set; }
    public virtual string RegularExpressionErrorMessage { get; set; }
}

MetadataProvider

protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
    //We only care about providing custom model metadata to PseudoObjectAttribute objects
    if ((containerType != typeof(PseudoObjectAttribute) && modelType != typeof(PseudoObjectAttribute)) || modelAccessor == null)
        return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

    ModelMetadata metadata = null;
    PseudoObjectAttribute attributeViewModel = null;
    System.Reflection.FieldInfo container = null;

    //The contents of this if statement allows me to get the PseudoObjectAttribute instance I need to work with.

    //This happens when we want metadata for the PseudoObjectAttribute type as a whole, not a specific attribute
    if (modelType == typeof(PseudoObjectAttribute) && containerType == null)
    {
        //
        if (modelAccessor.Target is ViewDataDictionary)
            attributeViewModel = (PseudoObjectAttribute)((ViewDataDictionary)modelAccessor.Target).Model;
        else
        {
            container = modelAccessor.Target.GetType().GetField("item");
            if (container != null)
            {
                attributeViewModel = (PseudoObjectAttribute)container.GetValue(modelAccessor.Target);
            }
            container = modelAccessor.Target.GetType().GetField("model");
            if (container != null)
                attributeViewModel = (PseudoObjectAttribute)container.GetValue(modelAccessor.Target);
        }
    }
    else if(!string.IsNullOrEmpty(propertyName))
    {
        if (modelAccessor.Method.Name.Contains("FromStringExpression"))
        {
            //This happens when we want metadata for a specific property on the PseudoObjectAttribute
            container = modelAccessor.Target.GetType().GetField("vdi");
            attributeViewModel = (PseudoObjectAttribute)((System.Web.Mvc.ViewDataInfo)container.GetValue(modelAccessor.Target)).Container;
        }
            //GetPropertyValueAccessor is used when you bind the posted back form
        else if (modelAccessor.Method.Name.Contains("FromLambdaExpression") || modelAccessor.Method.Name.Contains("GetPropertyValueAccessor"))
        {
            //Accessed property via lambda
            container = modelAccessor.Target.GetType().GetField("container");
            var accessor = container.GetValue(modelAccessor.Target);
            //Sometimes the property is access straight from the parent object for display purposes in the view ex. someRegistration["ProductId"].GuidValue
            //In these situations the access is the Registration object and is not something we can use to derive the attribute.
            if (accessor is PseudoObjectAttribute)
                attributeViewModel = (PseudoObjectAttribute)accessor;
        }
    }

    // At this point I have an instance of the actual PseudoObjectAttribute object I'm trying to derive Metadata for and can build my Metadata easily using it.
    // I'm using typeof (String) as a starting point to build my custom metadata from but it could be any value type if you wanted to befit from the defaults
    metadata = new ModelMetadata(this, typeof (PseudoObjectAttribute), modelAccessor, typeof (String), propertyName);

    // Be sure to store any of the information you've obtained here that is needed to derive validation rules in the AdditionalValues
    metadata.AdditionalValues.Add("Structure", attributeViewModel.Structure);

    //TODO: Populate the rest of the Metadata here....

    return metadata;
}

ValidatorProvider

public class PseudoObjectAttributeValidatorProvider : ModelValidatorProvider
{
    public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
    {

        if (metadata.ContainerType == typeof(PseudoObjectAttribute) && metadata.PropertyName == "StringValue")
        {
            PseudoObjectStructure  structure = null;
            try
            {
                if (metadata.AdditionalValues.Any())
                structure = (PseudoObjectStructure)metadata.AdditionalValues["Structure"];
            }
            catch (KeyNotFoundException) { }

            if (structure != null)
            {
                if (structure.IsRequired)
                    yield return new RequiredAttributeAdapter(metadata, context, new RequiredAttribute());

                if (structure.RegularExpression != null)
                    yield return new RegularExpressionAttributeAdapter(metadata, context, new RegularExpressionAttribute(structure.RegularExpression) { ErrorMessage = structure.RegularExpressionErrorMessage });
            }
        }
        else
            yield break;        
   }
}

I THINK, . -, .

-2

viewModel. (Name/Value), .

-, , : a) (Forms/Windows) b) , , , , //. , - . . ModelBinder - .

-, METADATA, , Touple (Of string, bool), , , . - ASP.NET MVC, MetaDataProvider. : http://buildstarted.com/2010/09/14/creating-your-own-modelmetadataprovider-to-handle-custom-attributes/

Last, DisplayTemplates EditorTemplates . . , , KeyValuePairs . METADATA ASP.NET MVC MetaDataProvider.

-

0

All Articles