Wednesday, 28 March 2012

Asp.net mvc DataAnnotation ValidateAttribute two properties comparison.

Using Datannotion is great but there are scenarious in which the current attributes compare, range etc becomes inadequate especially for the comparisions. So we create here our own custom validation using ValidationAttribute class which is the base class for all the annotation attributes. So by deriving from it and overriding the Isvalid method we can create our custom attribute for the model.

So here is the scenario in which I will be validating the Username against the password which should not be equal. Compare attribute cannot be used in this scenario so I have created a custom attribute for that. Here is the model with the attribute.

User View Model
    [CompareUserPass("UserName", "Password", ErrorMessage = "UserName and password cannot be equal")]
    public class UserView
    {
        [Required(ErrorMessage = "UserName Required")]
        public string UserName
        {
            get;
            set;
        }
        [Required(ErrorMessage = "Password Required")]
        public string Password
        {
            get;
            set;
        }
         
        public string ConfirmPassword
        {
            get;
            set;
        }
     }

Note in the model above I had covered two properties comparision here so the attribute is at the top of the class and to validate one property you specify the attribute with that property which I have not covered here.

Custom Validation attribute class
     [AttributeUsage(AttributeTargets.Class)]
     public class CompareUserPassAttribute : ValidationAttribute
     {
        private string Property1 { get; set; }
        private string Property2 { get; set; }

        public CompareUserPassAttribute(string PropertyName1, string PropertyName2)
        {
             Property1 = PropertyName1;
             Property2 = PropertyName2;
        }

        public override Boolean IsValid(Object value)
        {
            if (Property1 == null && Property2 == null)
                return true;
            else
            {
                PropertyDescriptorCollection propertiess = TypeDescriptor.GetProperties(value);
                object originalValue1 = propertiess.Find(Property1, true).GetValue(value);
                object originalValue2 = propertiess.Find(Property2, true).GetValue(value);
                if (!originalValue1.Equals(originalValue2))
                    return true;
                else
                    return false;
            }
        }
     }


View

@model MvcApplication22.Models.UserView
@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <title>Creates</title>
</head>
<body>
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
    @using (Html.BeginForm()) {
        @Html.ValidationSummary(true)
        <fieldset>
            <legend>UserView</legend>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.UserName)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.UserName)
                @Html.ValidationMessageFor(model => model.UserName)
            </div>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.Password)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Password)
                @Html.ValidationMessageFor(model => model.Password)
            </div>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.ConfirmPassword)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.ConfirmPassword)
                @Html.ValidationMessageFor(model => model.ConfirmPassword)
            </div>
    
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>
    }
    
    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>
</body>
</html>

Thursday, 22 March 2012

Asp.net mvc model binding security.

One of the thing that makes asp.net.mvc so interesting is default model binding. Model binding in simple words allows you to take the posted form data from the view and bind it to the action method's parameter in the controller without any fuss. But there is a security flaw in model binding which everyone using asp.net mvc should know .The problem is, in asp.net mvc controller you cannot be sure what you got as the posted value from the view because it is absolutely possible that an extra property, or an overwritten property which you don't want get passed to the controller which could spell disaster. And in the controller if the property matches the orginal property then the things could get out of hand.

Here's a simple scenario to understand more what I have defined. A person filling a create user form to become the member of the website requiring some payment in the process passed an Isenabled=true property (which we all have) and unfortunately there is a match in the model at the controller and you haven't defined any whitelist properties (which are included at the controller) or any black listed properties which (which we excluded at the controller) then the user will automatically become active. The cycle which was to be followed was that the moderator after viewing if everything was right and the payment is successfully accepted was going to enable him. So there is a voilation of that cycle.


Tools

You can create the scenario by using firebug.

Solution

By using Include or Exclude in the Bind Attribute class we can lock the properties that are allowed in the model.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreateUser([Bind(Include="UserName, Password,ConfirmPassword EmailAddress")] UserView User)
If there are large quantity of fields you have the option to use exclude to restrict the properties.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreateUser([Bind(Exclude="IsEnabled")] UserView User)
Other option
   [Bind(Exclude="GroupID")]
    public class Group
    { 
        [Key]
        public int GroupID { get; set; }

        [Required]
        [Display(Name = "Group Name")]
        [StringLength(50)]
        public string GroupName { get; set; }
        [ScaffoldColumn(false)]
        public DateTime? CreatedDate { get; set; }
        [ScaffoldColumn(false)]
        public DateTime? ModifiedDate { get; set; }
    }

Saturday, 17 March 2012

Asp.net mvc using AutoMapper simplified.

Most of the time in the real world applications it is not possible to map the database model to your presentation view directly because of the fact you may need some additional fields in your view. Using view Data or Viewbag is not always the good idea as it may make things harder or less elegant which are much easier to do.

We are creating the User create view as an example here in which confirm password field is additional field in the view and it has nothing to do with the database model. So in this case you would create one model for the database fields as usual and one extra model for the view. The problem is that in the controller you have to map each property from your model for the database to the model for the view manually which shouldn't be done because it is not a good practice as there is a tool for that. The third party tool the Automapper solves this problem by automatically mapping your view model which you get from the view in the controller to the database model.


User Table

UserName varchar(50)
password varchar(50)

User Model or database model
    public class User
    {
        public string UserName
        {
            get;
            set;
        }

        public string Password
        {
            get;
            set;
        }
    }
UserView Model for the presentation view
    public class UserView
    {
        [Required(ErrorMessage = "UserName Required")]
        public string UserName
        {
            get;
            set;
        }

        [Required(ErrorMessage = "Password Required")]
        public string Password
        {
            get;
            set;
        }

        [Compare("Password")]
        public string ConfirmPassword
        {
            get;
            set;
        }
    }
The generated view in which the model specified is the Userview not the User.

@model MvcApplication22.Models.UserView
@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <title>Creates</title>
</head>
<body>
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
    @using (Html.BeginForm()) {
        @Html.ValidationSummary(true)
        <fieldset>
           
    
            <div class="editor-label">
                @Html.LabelFor(model => model.UserName)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.UserName)
                @Html.ValidationMessageFor(model => model.UserName)
            </div>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.Password)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Password)
                @Html.ValidationMessageFor(model => model.Password)
            </div>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.ConfirmPassword)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.ConfirmPassword)
                @Html.ValidationMessageFor(model => model.ConfirmPassword)
            </div>
    
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>
    }
    
    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>
</body>
</html>


Below is the code without Automapper which is not a good practise.Two fields are not the problem but imagine having 15 fields to map which will be quite a task, the number of line will increase also the possibility of an error.

    public ActionResult Creates()
    {
         UserView UserViewModel = new UserView();
         return View("Creates",UserViewModel);
    }

    [HttpPost]
    public ActionResult Creates(UserView UserView)
    {
        // Valdations

        if (ModelState.IsValid)
        {
          User User = new User();
          User.UserName = UserView.UserName;
          User.Password = UserView.Password;
                          
          // Create User code            
        }
        return View();
    }
Now finally automapper in action below.

In global.asax you have to create a map before using it in the controller.

global.asax.

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        Mapper.CreateMap<UserView, User>();
        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);
    }


Controller.

    public ActionResult Creates()
    {
        UserView UserViewModel = new UserView();
        return View("Creates",UserViewModel);
    }

    [HttpPost]
    public ActionResult Creates(UserView UserView)
    {
        // Valdations
        if (ModelState.IsValid)
        {
           User User = new User();
           //  User.UserName = UserView.UserName;
           //  User.Password = UserView.Password;
           AutoMapper.Mapper.Map(UserView, User);
                          
           // Create User code            
         }
         return View();
     }