Securing objects in ViewModel - asp.net-mvc

I am trying to get security settings from the database for objects in a model. I would like to enable / disable / hide controls on my Rendered View depending upon the security settings for a user logged in.
This is what I have got so far:
public class RestrictedObjectsViewModel
{
[SecureObject(ObjectId = 1)]
[Display(Name = "Name")]
public string Name { get; set; }
[SecureObject(ObjectId = 2)]
[Display(Name = "Address")]
public string Address { get; set; }
[SecureObject(ObjectId = 3)]
[Display(Name = "Phone Number")]
public string PhoneNumber { get; set; }
}
Using this approach, I would be querying the database for each object being rendered. Is it possible to query the database just once for the entire objects in the model to get a list of permissions for objects? How would I set that?
UPDATE:
Ok, let me go a bit into detail.
In my code when I set the following attribute to an object, i have programmed my HTML to hide the associated table row for the rendered object:
[SecureObject(IsInvisible = true)]
The above code works correctly in my tests. However, when i try to do the following:
public class RestrictedObjectsViewModel
{
[SecureObject(IsInvisible = ObjectId3Invisible)]
[Display(Name = "Phone Number")]
public string PhoneNumber { get; set; }
public RestrictedObjectsViewModel(bool setPermissions = false)
{
if (setPermissions)
{
ObjectId3Invisible = true;
}
}
public bool ObjectId3Invisible = false;
}
I get an error message saying "An object reference is required for the non-static field, method, or property 'MyProject.Models.RestrictedObjectsViewModel.ObjectId3Invisible'"
Here is the controller:
public ActionResult RestrictedObjects()
{
return View(new Models.RestrictedObjectsViewModel(true));
}
If i change the ObjectId3Invisible to static, i will not be able to change the value to true or false during runtime.
Any suggestions?

Related

How to get the user logged in

I'm having an issue with trying to set the value to the user that logged in to the website. This is what I have so far but I'm getting this error
Error CS0200 Property or indexer 'Employee.getName' cannot be assigned to -- it is read only. What changes would I make to set the user that logged in the view
Employee Model
public class Employee
{
[Required]
[Display(Name="Employee Number")]
public int employeeNum { get; set; }
[Display(Name = "Employee First Name")]
public string firstName { get; set; }
[Display(Name = "Employee Last Name")]
public string lastName { get; set; }
[Display(Name = "Employee Department")]
public string department { get; set; }
[Display(Name = "Employee Name")]
public string Name
{
get
{
return string.Concat(firstName, " ", lastName);
}
}
public string getName
{
get {
IssueDAO dbObj = new IssueDAO();
dbObj.connectionString = "Server=tw-testdb-04;Database=TWCL_OPERATIONS;uid=sa;password=P#ssw0rd";
var emp= dbObj.getEmployee(employeeNum);
return emp;
}
}
}
}
Controller
private Requisition getRequisition
{
get
{
Requisition requisition = (Requisition)Session["Requisition"];
if (requisition == null)
{
requisition = new Requisition();
Session["Requisition"] = requisition;
}
return requisition;
}
}
public ActionResult RequisitionItem()
{
//Session.Clear();
//Set the document number and type to autoamtic values
IssueDAO dbData = new IssueDAO();
getRequisition.reqDate= DateTime.Now;
getRequisition.reqNumber= string.Concat("RN", DateTime.Now.ToString("yyyyMMddhhmmssms"));
getRequisition.count = 0;
getRequisition.inventory_account = 5520;
getRequisition.employeeDetails.getName = System.Web.HttpContext.Current.User.Identity.Name;
getRequisition.item = new Item();
return View(getRequisition);
}
Property or indexer 'Employee.getName' cannot be assigned to -- it is
read only.
The error is self explanatory. In your Employee class, you have defined getName with only get accessor method for this property. That means, the value of it can only read by some other code. You are trying to set the value to this property and hence the compiler is complaining about it.
If you want the value of this property to be settable by some other code, you should have a set access modifier on this property.
IMHO, you should keep your view models lean and simple. There should not be any data access code to get data inside a view model properties ( that is mixing 2 concerns ,UI and Data access together!)
I suggest you have a settable and gettable property in your view model to pass the logged in user name
public class Employee
{
// Your other properties
public string LoggedInUserName { set;get;}
}
Now you can set this as needed
var emp=new Employee();
emp.LoggedInUserName = "Any username value here";
or
emp.LoggedInUserName = System.Web.HttpContext.Current.User.Identity.Name;

Prevent to use default model data annotations in ViewModel

I started working on my first serious MVC project for school unfortunately without defining the data annotations to the model first (so did not set "required" annotation, limit to size of attribute names etc.). I work with a viewmodel, and after adding the annotations the model is causing my ViewModel state to get invalid when posting the form.
It seems like it's the email required that is causing the issue. It is not used on viewmodel and in the form and it seems the viewmodel expects it will get it. Is there a way the form to stop demanding this field by setting some limitation in viewmodel (or controller). I would really prefer not to change the structure of the application (if I start from the scratch I would probably do this a bit different, but not much time is left to finalize the project)
Customer (Model)
public Class Customer(){
public int Id { get; set; }
[Required(ErrorMessage = "Required")]
[StringLength(25, ErrorMessage = "Message"]
public string Name { get; set; }
public string Logo { get; set; }
//[Required(ErrorMessage = "Email required")]
//[Display(Name = "E-mail")]
//[RegularExpression(xxxx, ErrorMessage = "not correct")]
public string Email { get; set; }
public int UserId { get; set; }
}
ViewModel
public class CustomerEditViewModel
{
public Customer Customer { get; set; }
[FileTypes("jpg,jpeg,png")]
[FileSize(1024 * 1024, ErrorMessage = "Max x bytes")]
public HttpPostedFileBase File { get; set; }
}
You can remove errors from the modelstate in your controller, e.g.
this.ModelState[key].Errors.Clear();
where key is the bit to be cleared, so if it's email it's most likely -
this.ModelState["Customer.Email"].Errors.Clear();

How to make an array for timestamps on MVC 3

I am fairly new to MVC 3 and I was tasked with creating a to-do list, that has got timestamps for every independent variable, so when one variable changes, the timestamp of when it was changed would appear on the text field of that variable and not change the other timestamps of the other variables i.e each variable would have an individual timestamp. I believe I can only or most likely achieve this by creating an array. Any ideas on how I can carry this out?
I dummy code would be really appreciated
Here's a sample of my model, I followed this tutorial http://www.asp.net/mvc/tutorials/getting-started-with-aspnet-mvc3/cs/intro-to-aspnet-mvc-3
public class Checklist
{
public int ID { get; set; }
public string Title { get; set; }
public string Status { get; set; }
[Display(Name = "Start Date")]
public string Start_Date { get; set; }
[Display(Name = "Complesion Date")]
public string Complesion_Date { get; set; }
public DateTime[] Timestamp
{
get { return timestamp; }
set { timestamp = value; }
[Display(Name = "Internal Review System Reference")]
public string Internal_Review_System_Reference { get; set; }
[Display(Name = "Assignment from Original Owner")]
public bool Assignment_from_Original_Owner { get; set; }
public class listDBContext : DbContext
{
public DbSet<Checklist> List { get; set; }
}
And here's a sample of my controller code
public class SybreController : Controller
{
private listDBContext db = new listDBContext();
private Checklist check = new Checklist();
private string oldTitle { get; set; }
private string oldStatus { get; set; }
public ActionResult Edit(int id)// Called when edit button is clicked
{
Checklist checklist = db.List.Find(id);
this.oldTitle = checklist.Title;
this.oldStatus = checklist.Status;
//initAllArrays(checklist);//initialize our arrays with previous values
return View(checklist);
}
[HttpPost]
public ActionResult Edit(Checklist checklist)
{
if (ModelState.IsValid)
{
checklist.Timestamp = DateTime.Now;
if (checklist.Title != this.oldTitle)
{
checklist.stamps[0] = DateTime.Now;
}
if (checklist.Status != this.oldStatus)
{
checklist.stamps[1] = DateTime.Now;
}
else
{ checklist.stamps[1] = checklist.stamps[1]; }
db.Entry(checklist).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
Basically I need an timestamp for every individual variable in my model, so that when edited, the timestamp corresponded to when it was edited, the problem I've been facing is the timestamp variable changes across all the variables instead of only the one which was changed. I just need the program to print the former timestamp from when it was last edited, and if edited, display the current time along side the text field.
Hope you understand -__-
You can't solve your problem in this way. Asp.net MVC is stateless, it means that the instance of the controller is created per every request. It means that the checks that you have performed to set time stamps have always true value, as oldTitle and oldStatus are nulls.

MVC.net EF validation when using View Models

I'm working on an MVC application and i'm trying to implement some validation. I've strucuture the site to use EF for storage and a set of view models with automapper.
I want to add some validation which i'm sure would work if i added it to the View Models however i'm assuming it would be better to put validation in with the EF model so if in the future i create another interface the same validation would also apply.
First of is this the correct approach and second how do i get MVC to actually test the validation before saving the object. Currently it just skips my EF validation.
The address model is auto generated so i created this partial class to add the validation:
public partial class Address : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(this.AddressLine1) &&
!string.IsNullOrWhiteSpace(this.AddressLine2) &&
!string.IsNullOrWhiteSpace(this.AddressLine3) &&
!string.IsNullOrWhiteSpace(this.Town) &&
!string.IsNullOrWhiteSpace(this.City) &&
!string.IsNullOrWhiteSpace(this.County) &&
!string.IsNullOrWhiteSpace(this.Postcode))
yield return new ValidationResult("Address cannot be blank.");
}
}
This is my view model class with the display names changed
public class AddressVM
{
public int? ID { get; set; }
[Display(Name = "Address line 1")]
public string AddressLine1 { get; set; }
[Display(Name = "Address line 2")]
public string AddressLine2 { get; set; }
[Display(Name = "Address line 3")]
public string AddressLine3 { get; set; }
[Display(Name = "Town")]
public string Town { get; set; }
[Display(Name = "City")]
public string City { get; set; }
[Display(Name = "County")]
public string County { get; set; }
[Display(Name = "Postcode")]
public string PostCode { get; set; }
}
This is my controller
public ActionResult AddAddress(AddressVM vm)
{
IncidentAddress theAddress = Mapper.Map<AddressVM, Address>(vm);
if (ModelState.IsValid)
{
UOW.Addresses.Add(theAddress);
UOW.Save();
}
return PartialView("AddressVM-edit", vm);
}
if (ModelState.IsValid)
This will always be true for your object, as it will look for validity of your model, which is AddressVM (you receive that from view so this is your model) and does not have any validators. ModelState does not know that you have mapped this object to some other which implements validation. You need to run validation on your other object manually and add validation errors to ModelState.
If you want to have this separated, you can implement IValidatableObject on AddressVM, and internally perform validation by creating a instance of Address, mapping it from AddressVM (this) and returning result of it's Validate method. You also can expose the same constructed Address object as a property and use it to perform entity operation.
Example of AddressVM:
public class AddressVM : IValidatableObject
{
public int? ID { get; set; }
[Display(Name = "Address line 1")]
public string AddressLine1 { get; set; }
[Display(Name = "Address line 2")]
public string AddressLine2 { get; set; }
[Display(Name = "Address line 3")]
public string AddressLine3 { get; set; }
[Display(Name = "Town")]
public string Town { get; set; }
[Display(Name = "City")]
public string City { get; set; }
[Display(Name = "County")]
public string County { get; set; }
[Display(Name = "Postcode")]
public string PostCode { get; set; }
//// I added this and interface in class definition:
public IncidentAddress GetIncidentAddress()
{
return Mapper.Map<AddressVM, Address>(this);
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return this.GetIncidentAddress().Validate(validationContext);
}
}
This way your logic stays in your business object, and your viewmodel uses it without having copy of it or some other dependency.
The Address class and AddressVm are not bound to each other in your case - AutoMapper does not do validation stuff, it just copies values. So you do not get ModelState populated and validations performed.
There're two workarounds i'm thinking of
Define the validations on AddressVm. If ModelState.IsValid, then map AddressVm to Address and save.
You do not need AddressVm at all. Change Action signature to expect Address parameter. That way, ModelState.IsValid will be automatically populated by validation system (Not the best solution).
Ideally, ViewModels should be defined for specific scenarios. In your case, I would define AddAddressModel, use it only for adding addresses and define only the properties needed to create address. Then, define validations on AddAddressModel and use mapper to map ViewModel to Address instance (So, I prefer first solution, plus defining specific model).
If you need reusable validator classes, you could check out FluentValidation. It has good support of asp.net-mvc too.

MVC validation for unique

Hi I have an MVC app that I used to insert update invoices:
public class Invoice : IEntity, IValidatableObject
{
public virtual int InvoiceId { get; set; }
[Required(ErrorMessage = "Invoice Number is a required field.")]
[Column(TypeName = "varchar")]
[StringLength(20)]
[Display(Name = "Invoice Number:")]
public virtual string InvoiceNumber { get; set; }
[Required(ErrorMessage = "Organisation is a required field.")]
[Display(Name = "Organisation:")]
public int OrganisationId { get; set; }
...
}
The problem is I have a requirement "The combination of organisation and invoice number must be unique.
So this has been set up by the DBA in the database. So if I try to do this it will return an exception.
Is there a way instead of displaying the exception to catch it and add a custom error message to the validation summary?
You could catch the exception and add the error to the model state so that your client code handles all errors the same way. Something like:
ModelState.AddModelError("InvoiceNumber", "The combination of organisation and invoice number must be unique.");
you can achieve this by putting remote attribute on both properties and in AdditionalField parameter you pass the name of other property like
[Remote("IsUnique","home",AdditionalFields = "OrganisationID",ErrorMessage = "abc")]
virtual string InvoiceNumber { get; set; }
[Required(ErrorMessage = "Organisation is a required field.")]
[Display(Name = "Organisation:")]
[Remote("IsUnique","home",AdditionalFields = "InvoiceNumber",ErrorMessage = "abc")]
public int OrganisationId { get; set; }
and you can write InUnique method in home controller (for instance) like
public JsonResult IsUnique(string InvoiceNumber, int? OrganisationID)
{
if(InvoiceNumber == null || !Organisation.HasValue)
{
return Json({valid = true});//null check is not job of this attribute
}
else
{
bool result = CheckDbForUniqueness(InvoiceNumber, OrganisationID.Value);
return Json({valid = result});
}
}
This method will be invoked when you change value of either inputs on the form and take the other value as parameter. if either value is null it would return true and null checking will be handled by Required attributes.
Without getting fancy...you could catch it in the controller, then display the message in the view via a member of the model or ViewBag/ViewData.
You can also write a custom validator that checks this for you (even with some client-side validation as well). I wrote one that checked a database for whether a username existed or not, a similar check could be done for this, without relying on exceptions (although you still would want to handle it I wouldn't recommend using exceptions for a normal course of action).

Resources