Preventing tampering of form fields in ASP.NET MVC EF - asp.net-mvc-3

The default strongly-typed Edit page in ASP.NET MVC 3 generally exposes all fields for the Entity. While this is often workable, some fields are a security risk. For example a simplified magazine subscription entity might look like:
public void Subscription() {
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public DateTime SubscribedThru { get; set; }
}
If I provide an Edit page to let users change their own address, for example, it's a security risk to include the SubscribedThru field because a knowledgeable and malicious user could give themselves a free 10-year subscription by faking the date (even if I use #Html.HiddenFor(model => model.SubscribedThru). So I am not including that field in any way on the Edit page html (via razor).
I thought the answer might be to prevent binding attempts on SubscribedThru on the Edit method in the controller using something like:
[HttpPost]
public ActionResult Edit([Bind(Exclude="SubscribedThru")] Subscription subscription) {
if (ModelState.IsValid) {
db.Entry(subscription).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
return View(subscription);
}
When I get to the SaveChanges(); line, it throws the error The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value. I believe that the SubscribedThru date (properly?) doesn't exist, and the empty value is less than SQL Server can handle. What surprises me is that it's even trying to update that field when I have Binding excluded for it.
So far my best solution seems to be to create a custom ViewModel that omits the SubscribedThru date, but that seems a lot of duplication of fields, validation, etc.; if possible I'd like to just make the one field SubscribedThru safe from user editing.
I can't say I fully understand the UpdateModel and TryUpdateModel methods and wonder if that's a direction to head? I played with them and EF throws errors for having duplicate objects (same key) which is perplexing.
Also, I'm not clear if the subscription data is preserved from the initial load in public ActionResult Edit(int id) in the controller all the way to the final [HttpPost]
public ActionResult Edit(Subscription subscription)... method, or does the line db.Entry(subscription).State = EntityState.Modified; try and set all the data (I thought it was just setting a flag indicating "edited-so-EF-should-save-this").
I'm a long-time .NET developer, just jumping in to my first ASP.NET MVC project, so I'm probably overlooking something painfully obvious. Thanks for any help!

So far my best solution seems to be to create a custom ViewModel that omits the SubscribedThru date, but that seems a lot of duplication of fields, validation, etc.;
That is exactly what you should do to keep things neat & tidy. AutoMapper eases the ViewModel variation headache.

This page contains an example of updating a model using TryUpdateModel (Listing 4):
http://www.asp.net/mvc/tutorials/older-versions/models-(data)/creating-model-classes-with-the-entity-framework-cs
You can whitelist only the fields that you allow to be edited, which removes the security risk.

Related

Using a viewmodel which ignores the properties from the model

I'm using entity framework and MVC (both CORE if that matters) to make a site. Using the model and tying it directly to the view is fine all the CRUD actions work, everything is lovely.
I wanted to use a couple of pages to access the model so the site looked better, so split the controls out onto separate views and added a corresponding viewmodel for each, so my project looks like this
-Model
--CustomerModel
-ViewModel
--CustomerNameVM
--CustomerAddressVM
-View
--CustomerNameView
--CustomerAddressView
The CustomerModel has a number of properties
Forename
Surname
Address
Postcode
with Forename and Surname in the CustomerNameVM and Address and Postcode in CustomerAddressVM. Surname is defined as [Required] in the model but not in CustomerNameVM which I believe is the correct way to do it.
I'm struggling to get the model loaded into the viewmodel and then trying to save it when I'm editing the address details in CustomerAddressView because it errors when I try and save as the viewmodel doesn't contain Surname (from the model), so it's null and therefore the [Required] criteria isn't being met.
I've tried a few methods of trying to get past this like :-
Jeffrey Palermo's Onion Architecture
Repositories
domain models
amongst others which all end up with the same problem, I can't save the Address as the Surname is null.
How do I ignore validation criteria for the properties of the model that aren't being referenced in the viewmodel?
Or how do I load and reference only those properties of the model that are present in viewmodel?
Edit
For those who keep asking for code, which codeset? I've tried 30 of them now, none of which do the job. Which one of these do you want? I'm trying to get a general idea of how this is supposed to work as none of the methods, documentation and associated examples function.
Here's a starter for 10, it's unlike the other 29 codesets but it's code and probably the shortest.
The controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Step2Address(int? id, [Bind("CustomerID,txtAddress,txtPostcode")] VMAddress VMAddress) {
if (ModelState.IsValid) {
//the saving code
}
return View(VMAddress);
}
the model
public class clsCustomer {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CustomerID { get; set; }
public string Forename { get; set; }
[Required]
public string Surname { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
the viewmodel
public class VMAddress {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CustomerID { get; set; }
public string Address { get; set; }
public string Postcode { get; set; }
}
the view
#model theProject.Models.VMStep2Contact
<form asp-action="Step2Address">
<input type="hidden" asp-for="ComplaintID" />
<input asp-for="txtAddress"/>
<input asp-for="txtPostcode"/>
<input type="submit" value="Save" />
the context
public class ContextCustomer : DbContext {
public ContextCustomer(DbContextOptions<ContextCustomer> options) : base(options) {
}
public DbSet<clsCustomer> Customer{ get; set; }
}
Clicking "Save" on the webpage calls the controller straight away, which hits the first line if (ModelState.IsValid) and as the Surname isn't set and is [Required] the ModelState is not valid so no save is attempted.
I don't actually understand what the problem is, and without code, it's impossible to say what you might be doing wrong. Generally speaking, you shouldn't have any issues since you're using view models.
A view model is, of course, not saved directly to the database, so it has to be mapped over to an actual entity class that you will be saving. In the case of an edit, you should retrieve all relevant entities from the database, map any posted values onto the appropriate properties on those entities, and then save those entities back to the database. If you're doing this, presumably, the customer model should already contain the Surname and other required properties, and you'd only be modifying/setting the address properties.
If you're doing a create, then, simply you can't just take address information. You need the name as well, so for this, you'd need to pass a view model that contains at least all required fields, such that you have all the information you need to actually save the entity.
If you're trying to do a multi-step form, where you collect all the information over multiple posts, then you simply must persist the posted data somewhere other than the database until you have enough of it to actually save an entity to the database. This is usually accomplished via Session/TempData.

EF, POCO, DB First... how to do Business Logic in Property "set"?

OK, so I've been building my first large(ish) EF 4.1 POCO + MVC application. It's a replacement of a legacy system so I 'm using an existing database.
I've generated my POCO classes using DbContext T4 generation. I've got some really nice forms going on and some really nice validation happening with a lot of sexy generics in my MVC classes to cut down on boiler-plate code... All's good.
Suddenly I realized that the most sensible thing (to me) would be for some of business logic to be in the "set" of some of the properties of my POCO objects.
E.g. Suppose the following class was generated by the T4;
public partial class SalesOrderLine
{
public int ID { get; set; }
public int SalesOrderID { get; set; }
public int ProductID { get; set; }
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public decimal ExtendedPrice { get; set; }
public virtual Product Product { get; set; }
public virtual SalesOrder SalesOrder { get; set; }
}
Ignore for a moment the obvious argument that the calculated field "ExtendedPrice" shouldn't even be stored in the database, and just come along with me for the ride...
...then, it seems to me, logically, if this object is really supposed to represent a Sales Order Line, that I should be able to construct my object such that the following unit test will work:
SalesOrderLine sol = new SalesOrderLine();
sol.UnitPrice = 100;
sol.Quantity = 5;
Assert.IsEqual(sol.ExtendedPrice, 500);
...obviously I can't do that as long as I want the base POCO to be generated by the T4. It seems to me I have several options:
Set the generated code file's properties "do not compile", copy and paste the generated code into another file and modify the "set" to do the business logic of setting the extended price when the UnitPrice or Quantity is set. The downside here is that the logic will be run whenever an object is loaded from the database (since the EF will set the public properties and not my private fields). Additionally, this object will then need to be maintained manually for the rest of the life of the project when database changes occur.
Create an UpdateTotals function that gets called in the Validate routine that I have for my object, which gets called by the SaveChanges() on the DbContext. Obviously, the above Unit Test above would not work in that case. The system, and my integration tests however would work and would only call the code when a change was done to the object.
Decide that I'm asking the wrong question, and that I should really add methods to the object called "SetPrice" and "SetQuantity", and then qualify the set accessors of the UnitPrice and Quantity to be "internal". The downside here is that MVC will try and update the model from the form and won't be able to set those properties.
Some solution that involves downloading two or three more frameworks that create even more levels of abstraction than I already have... A repository pattern, or "use NHibernate" or something like that... You can suggest this, but I'm growing weary of how much work it is to set things up to do it the "academically correct" way. For this project, I'd rather meet halfway on the long-term-maintainability vs. speed-of-development spectrum and not over-complicate my project with a ton of extra tools and dlls... ...but I'll try an keep an open mind :)
--- EDIT: another idea ---
[5.] Another thought, since the fields are always simply calculated there should really be no need to ever set them - either from the database or otherwise. Therefore, something like this might work:
public decimal ExtendedAmount
{
get { return UnitPrice * Quantity; }
internal set { }
}
...my thought is that the EF instantiation would attempt to call the "set", but the set would do nothing, then, when the object was saved or checked for changes it would call the 'get' and that would return the calculated value and that value would get stored in the DB. The only downside here is when you were trying to use the object model to validate the database when the database had in incorrect value stored in the ExtendedAmount field. It's a little hokie, I know, but I thought it would be an interesting trick... in fact the "set" could perhaps throw an exception if (value != UnitPrice * Quantity)
--- END EDIT ---
I'm curious to hear what other have done in these kinds of cases, as I'm sure it's common. Seems like a lot of the tutorials take you as far as "generating POCO classes from the database", and then leave the rest of the project development up to you.
Cheers,
Chris
A couple ideas:
Why not use Code First? That way, you can put business logic (e.g., calculated properties) right in your entity class.
Example
public partial class SalesOrderLine
{
public int ID { get; set; }
public int SalesOrderID { get; set; }
public int ProductID { get; set; }
private decimal _unitPrice;
public decimal UnitPrice
{
get { return _unitPrice; }
set
{
if (value == _unitPrice) return;
_unitPrice = value;
CalculateExtendedPrice();
}
}
private decimal _quantity;
public decimal Quantity
{
get { return _quantity; }
set
{
if (value == _quantity) return;
_quantity= value;
CalculateExtendedPrice();
}
}
public decimal ExtendedPrice { get; set; }
public virtual Product Product { get; set; }
public virtual SalesOrder SalesOrder { get; set; }
private void CalculateExtendedPrice()
{
ExtendedPrice = UnitPrice * Quantity;
}
}
If Code First is not an option, what about making your entity a partial class (if it is not already) and putting your business logic in a separate code file (but with the same class name). This way, your main code file will get overwritten when you generate, but your secondary code file will remain. This is the usual way to deal with generated code.

How to find properties are not modified or change?

I have viewmodel which i am binding with my view call "EditProfile". Now once the user click on the "EditProfile" link from the menu bar I load all the profile information by calling get restful service and user can see his/her profile.
User can change any field they want but I want to post only fields that are changed by User.
I know how to compare two properties but don't know how to pull only modified properties from the viewmodel. Have anybody arrived to this situation before?
Properties in my viewmodel are:
public class UserViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Profession { get; set; }
}
In the past I implemented INotifyPropertyChanged for the ViewModel. And I logged which property's changed after the PropertyChanged event fired.
Why not take an alternative approach - post everything (so the model is available) and simply have "IsDirty" properties on your objects that are smart enough to submit only changes to the database (like the entity framework does by default)
You can simply form your own jQuery .post() parameters, but unless you have a ton of data - why? It makes for a difficult to read view model (how do you know what is posted under what scenarios) so the typical approach is to post separate view models or post everything which in my opinion for basic forms is easier.

MVC3 - Recommended way to create fields for IEnumerables with Editor Templates

I want to create a form for an entity. One of the members is an IEnumerable of a different type (that also has an IEnumerable member), for example:
public class Person
{
public string Fullname { get; set; }
public int Age { get; set; }
public IEnumerable<Position> Jobs { get; set; }
}
public class Position
{
public string Title { get; set; }
public IEnumerable<string> PhoneNumbers { get; set; }
}
I'm trying to find a good example of creating multiple fields in the html, how would I allow to enter more than one position? I think I read somewhere about someone who's cloning that part of the form with jQuery - is that the way to go?
Thanks
This blog post talks about editing a variable length list and may be what you are after.
Possible duplicate of Editing a Variable Length List, ASP.NET MVC 3 Style with Table
I personnally use an improved version of BeginCollectionItem, but I find it still too complicated when used with child views.
This is a great fail of ASP.NET MVC promises (it should be simple, fluid and powerful).

ASP.Net MVC 3 DataAnnotations validating List<T>

I have a ViewModel that has been deserialized from JSON which looks something like this:
public class UserThingsUpdateViewModel
{
public IList<Thing> Things { get; set; }
[Required]
public int UserId { get; set; }
public int Age { get; set; }
}
Thing is also a ViewModel which also has various DataAnnotaion ValidationAttribute attributes on the properties.
The problem is that Lists don't seem to get validated and even after a through search I cant seem to find any articles that tackle this. Most suggest that the ViewModel is wrong if it includes a list.
So, what is the best way to validate my list and add Model Errors to the Model State?
Prior to checking ModelState.IsValid, you could add code to step through and validate each Thing, as follows:
foreach (var thing in Things)
TryValidateModel(thing);
This will validate each item, and add any errors to ModelState.
You could write a custom validator attribute and decorate the list property with it? That would allow you to write custom logic to get the elements out of the list and validate them.

Resources