Replace parameter in an action filter - c#

I am trying to remove the password captured in the parameters from my action filter and replace it with the word "Removed", in order for the parameters to be stored in the database for logging. The password is stored in a ViewModel (depending on the action). Below is sort of a "pseudo-code" as to what I am trying to achieve.
How would I go about masking/replacing the password to be saved in the database? The main issue I am having is that I do not know how to access the password parameter and change it. I have tried getting it using the actionParams.TryGetValue("model, out value) but the problem is that I do not know the type of value and it changes depending on the action. Also, I am unable to call many methods on actionParams["model"] (such as contains)
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var actionParam = filterContext.ActionParameters;
// Remove the password from the parameters
if (actionParam.ContainsKey("model") && actionParam["model"] != null)
{
// If actionParam["model"].ToLower().Contains("password")
// actionParam["model"]["password"] = "Removed";
// If actionParam["model"].ToLower().Contains("confirm password")
// actionParam["model"]["confirm password"] = "Removed";
}
string str = Json.Encode(filterContext.ActionParameters).Trim();
string par = string.Empty;
if (str.Length > 2)
{
par = str.Substring(1, str.Length - 2).Replace("\"", string.Empty);
}
ActionLog log = new ActionLog()
{
SessionId = filterContext.HttpContext.Session.SessionID,
UserName = (request.IsAuthenticated) ? filterContext.HttpContext.User.Identity.Name : "Anonymous",
Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
Action = filterContext.ActionDescriptor.ActionName,
ActionParameters = par,
IsPost = request.HttpMethod.ToLower() == "post" ? true : false,
IPAddress = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress,
UserAgent = request.UserAgent,
ActionDate = filterContext.HttpContext.Timestamp
};
//Store the Audit into the Database
ActionLogContext context = new ActionLogContext();
context.ActionLogs.Add(log);
context.SaveChanges();
// Finishes executing the Action as normal
base.OnActionExecuting(filterContext);
}
Example of possible view models
public class LoginViewModel
{
[Required]
[Display(Name = "User ID")]
[RegularExpression("^[a-zA-Z0-9]+$", ErrorMessage="Letters and Numbers Only")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
}
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
Example of possible action parameters
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)

One approach would be to use an interface as an abstraction so you are not dealing directly with a ViewModel. First, create some interfaces for the action filter to interact with.
public interface IPassword
{
string Password { get; set; }
}
public interface IConfirmPassword
{
string ConfirmPassword { get; set; }
}
Next, make your ViewModel classes implement those interfaces.
public class LoginViewModel : IPassword
{
[Required]
[Display(Name = "User ID")]
[RegularExpression("^[a-zA-Z0-9]+$", ErrorMessage = "Letters and Numbers Only")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
}
public class ResetPasswordViewModel : IPassword, IConfirmPassword
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
Then it is just a matter of updating your filter code. The filter doesn't need to know anything more about your model other than the fact that it implements IPassword or IConfirmPassword, which it can check with a cast.
Of course, for it to work correctly, you have to restore the original values before executing the action method (or alternatively do the logging after the action is run) so the action method will have the correct values.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var actionParam = filterContext.ActionParameters;
IPassword password = null;
IConfirmPassword confirmPassword = null;
string originalPassword;
string originalConfirmPassword;
// Remove the password from the parameters
if (actionParam.ContainsKey("model") && actionParam["model"] != null)
{
// If the model doesn't implement the interface, the result
// here will be null.
password = actionParam["model"] as IPassword;
confirmPassword = actionParam["model"] as IConfirmPassword;
}
if (password != null)
{
// Store the original value so it can be restored later
originalPassword = password.Password;
password.Password = "Removed";
}
if (confirmPassword != null)
{
// Store the original value so it can be restored later
originalConfirmPassword = confirmPassword.ConfirmPassword;
confirmPassword.ConfirmPassword = "Removed";
}
string str = Json.Encode(filterContext.ActionParameters).Trim();
string par = string.Empty;
if (str.Length > 2)
{
par = str.Substring(1, str.Length - 2).Replace("\"", string.Empty);
}
ActionLog log = new ActionLog()
{
SessionId = filterContext.HttpContext.Session.SessionID,
UserName = (request.IsAuthenticated) ? filterContext.HttpContext.User.Identity.Name : "Anonymous",
Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
Action = filterContext.ActionDescriptor.ActionName,
ActionParameters = par,
IsPost = request.HttpMethod.ToLower() == "post" ? true : false,
IPAddress = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress,
UserAgent = request.UserAgent,
ActionDate = filterContext.HttpContext.Timestamp
};
//Store the Audit into the Database
ActionLogContext context = new ActionLogContext();
context.ActionLogs.Add(log);
context.SaveChanges();
// Restore the original values
if (password != null)
{
password.Password = originalPassword;
}
if (confirmPassword != null)
{
confirmPassword.ConfirmPassword = originalConfirmPassword;
}
// Finishes executing the Action as normal
base.OnActionExecuting(filterContext);
}

Related

error "System.ArgumentNullException: 'Value cannot be null. Parameter name: collection' "" ASP:NET MVC C# Visual Studio 2017 dataTables

I have a dataTable in which I have a view to edit the data.
It works but not when I add in my model some Requirements like:
public class ModelTemplateEmail
{
[Display(Name = "EmailId")]
public int EmailId { get; set; }
[Display(Name = "UserName")][StringLength(20, ErrorMessage = "Do not enter more than 20 characters")]
[MaxLength(20)]
[Required(ErrorMessage = "Please enter a User name")]
public string userName { get; set; }
[Display(Name = "Email")]
[DataType(DataType.EmailAddress)]
[Required(ErrorMessage = "Please enter an Email")]
[RegularExpression(#"^\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*$", ErrorMessage = "Email is not valid.")]
public string Email { get; set; }
[Display(Name = "Title")]
public string Title { get; set; }
[Display(Name = "Description")]
public string description { get; set; }
public List<ModelTemplateEmail> EmailDataList { get; set; }
[Key]
public int languageID { get; set; }
[Display(Name = "Language")]
public string MainLanguage { get; set; }
}
I get an error
System.ArgumentNullException: 'Value cannot be null.
Parameter name: collection'
when debugging in my view in one dropDown list Which is not de data I put the requirements so I don't understand why is this error and how to solve it.
The string that have the error is:
#Html.DropDownListFor(model => model.languageID, new List<SelectListItem>(ViewBag.MainLanguage), new { #class = "form-control" })
The controller of my DropDwnList is:
public void MainLanguagelist()
{
var sqlstring = string.Format("SELECT [languageID], [MainLanguage] FROM [dbo].[Language]");
var myConnection = getconection();
SqlCommand myCommand = new SqlCommand(sqlstring, myConnection);
myCommand.ExecuteNonQuery();
SqlDataAdapter Language = new SqlDataAdapter(myCommand);
DataSet setLanguageData = new DataSet();
Language.Fill(setLanguageData);
ViewBag.MainLanguageList = setLanguageData.Tables[0];
List<SelectListItem> MainLanguageList = new List<SelectListItem>();
foreach (DataRow MainLanguage in ViewBag.MainLanguageList.Rows)
{
MainLanguageList.Add(new SelectListItem {Text = #MainLanguage["MainLanguage"].ToString(),
Value = #MainLanguage["languageID"].ToString()});
}
ViewBag.MainLanguage = MainLanguageList;
try
{
myConnection.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
Action result:
public ActionResult EditData(int EmailId, string userName, string Title, string Email, string description, int languageID)
{
ModelTemplateEmail Editdata = new ModelTemplateEmail
{
EmailId = EmailId, userName = userName, Email = Email, description = description,
Title = Title, languageID = languageID
};
MainLanguagelist();
return View(Editdata);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditData(ModelTemplateEmail modelEmail)
{
UpdateDataBase(modelEmail.EmailId, modelEmail.userName, modelEmail.Title,
modelEmail.Email,modelEmail.description, modelEmail.languageID);
return View ();
}
I fixed it adding MainLanguagelist(); in my http post method.

NotMapped prop Validation failed for one or more entities. See 'EntityValidationErrors' property for more details

good day ..
i created a model that has an property with [Notmapped] DataAnnotations and i created another class inherit from this model with same property but i add required DataAnnotations the problem is when i delete i got error "Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
"
My Model :
[Key]
[Display(AutoGenerateField = true, AutoGenerateFilter = true, Description = "IDDescription", GroupName = "IDGroupName", Name = "IDName", ShortName = "IDShortName", Prompt = "IDPrompt", Order = 50, ResourceType = typeof(Resources.BaseEntity))]
public long ID { get; set; }
[StringLength(207, ErrorMessageResourceName = "StringTooMuch", ErrorMessageResourceType = typeof(Resources.BaseSlider))]
[Required(AllowEmptyStrings = false, ErrorMessageResourceName = "DetailsRequired", ErrorMessageResourceType = typeof(Resources.BaseSlider))]
[Display(Name = "Description", ResourceType = typeof(Resources.BaseSlider))]
public string Description { get; set; }
[NotMapped]
public string ShortDescription
{
get
{
if (Description.Length <= 207)
{
return Description;
}
return Description.Substring(0, 207);
}
}
[Display(Name = "HasBTN", ResourceType = typeof(Resources.BaseSlider))]
public bool HasBTN { get; set; }
[Display(Name = "Is Image Dark")]
public bool IsDark { get; set; }
[Display(Name = "Link", ResourceType = typeof(Resources.BaseSlider))]
public string Link { get; set; }
[Display(Name ="Slider Type")]
public long SliderTypeID { get; set; }
[NotMapped]
//[ImageValidation(".jpg,.png,.japg", OriginalWidth = 1920, OriginalHeight = 600)]
[Display(AutoGenerateField = true, AutoGenerateFilter = true, Description = "ImagePathDescription", Name = "ImagePathName", ResourceType = typeof(Resources.BaseMore))]
public virtual HttpPostedFileBase ImagePathFile { get; set; }
#endregion
#region Relations
public virtual IList<BaseSliderPhotoUpload> Photos { get; set; }
public virtual BaseLookup SliderType { get; set; }
#endregion
public BaseSlider()
{
Photos = new List<BaseSliderPhotoUpload>();
}
and the class i created :
public class BaseSliderCreate : BaseSlider
{
#region Data
[NotMapped]
[Required]
//[ImageValidation(".jpg,.png,.japg", OriginalWidth = 1920, OriginalHeight = 600)]
[Display(AutoGenerateField = true, AutoGenerateFilter = true, Description = "ImagePathDescription", Name = "ImagePathName", ResourceType = typeof(Resources.BaseMore))]
public override HttpPostedFileBase ImagePathFile { get; set; }
#endregion
}
in delete actionresult code :
public ActionResult DeleteConfirmed(Guid id)
{
BaseSlider SliderObject = db.Sliders.Where(x => x.GUID == id && x.Deleted == null).FirstOrDefault();
SliderObject.Deleted = DateTime.Now;
SliderObject.DeletedByID = _CurrentUser.ID;
// Delete All Photos
DeletePhoto DeletePhoto = new DeletePhoto();
var DeletedPhotoName = new List<string>();
foreach (var name in SliderObject.Photos)
{
DeletedPhotoName.Add(name.FileName);
}
if (DeletePhoto.PhotoDeleted("Slider", DeletedPhotoName))
{
try
{
db.SliderPhotos.RemoveRange(SliderObject.Photos);
db.Entry(SliderObject).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception ex)
{
ErrorList.Add(ex.Message);
throw;
}
}
else
{
ErrorList.Add(DeletePhoto.ErrorMessage);
}
ViewBag.ErrorList = ErrorList;
return RedirectToAction("Delete", new { id = SliderObject.GUID });
}
when i save change i got error
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
i checked i found the EntityValidationErrors is that ImagePathFile is required..
thanks for helping my and i apologist for my bad English

Mvc Remote Attribute is sending "undefined" in query string

I'm trying to remote validate some code and for the parameter, its passing undefined in as a parameters. Here is my validation code:
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public class ValidationController : Controller
{
public JsonResult IsUserNameAvailable(string userName, int? UserId)
{
var users = new BusinessLayer.BdsAdmin.Users();
if (UserId == null || UserId == 0)
// Do something
else // Do something else
if (users.Count == 0)
{
return Json(true, JsonRequestBehavior.AllowGet);
}
string msg = string.Format("{0} is already taken and is not available.", userName);
return Json(msg, JsonRequestBehavior.AllowGet);
}
}
Here is my model:
public class EditUserAdministrationViewModel
{
public int UserId { get; set; }
[Required(ErrorMessage = "You must enter a user name.")]
[Display(Name = "User Name")]
[Remote("IsUserNameAvailable", "Validation", AdditionalFields = "UserId")]
public string UserName { get; set; }
// More properties
}
Looking at the request in Fiddler, here is what I see:
GET /Validation/IsUserNameAvailable?UserName=sara&UserId=undefined
Why is MVC injecting the string undefined into the request instead of the actual UserId?
You need to add
#Html.HiddenFor(m=>m.UserId)
at the view so that the binder will bind it to the remote validation controller or otherwise there is no value to bind

code first Entity Framework not saving to database

Ok, I am doing a project with entity framework 6. I have my class laid out. When I try to add the information to the database; it gives me following errors:
The best overloaded method match for 'System.Data.Entity.DbSet<img_site_codefi.DAL.DefaultConnection>.Add(img_site_codefi.DAL.DefaultConnection)' has some invalid arguments
Argument 1: cannot convert from 'AnonymousType#1' to 'img_site_codefi.DAL.DefaultConnection'
Here is my controller:
public ActionResult Contact(customer cust)
{
try
{
if (ModelState.IsValid)
{
cust.Tele_comp();
saveIntoDb(cust); // database
SendMail(cust); // mail sender
return RedirectToAction("Submited", "Home");
}
return null;
}
catch (DataException)
{
return View(cust);
}
}
private void saveIntoDb(customer cust)
{
using (var cust_In = new DefaultConnection())
{
var customer = new {fname = cust.fname,lname = cust.lname, tele = cust.tele, email = cust.email, reasn = cust.reasn };
//cust_In.customers.Add(customer); //HERE IS THE ERROR!!!
cust_In.SaveChanges();
}
}
and here is the model:
[Key]
[] // how to assign a number automatically
public int Cust_Id { get; set; }
[Required(ErrorMessage = "first name is required!")]
[Display(Name = "First name")]
public string fname { get; set; }
[Display(Name = "Last name")]
public string lname { get; set; }
[Required(ErrorMessage = "area code is required!")]
[StringLength(3)]
[RegularExpression(#"^[0-9]{3,}$", ErrorMessage = "Minimum 3 numbers required & contain only numbers")]
[Display(Name = "Telephone")]
public string tel_area { get; set; }
[Required(ErrorMessage = "first three numbers are required!")]
[StringLength(3)]
[RegularExpression(#"^[0-9]{3,}$", ErrorMessage = "Minimum 3 numbers required & contain only numbers")]
public string fir_thr_tel { get; set; }
[Required(ErrorMessage = "last four numbers are required!")]
[StringLength(4)]
[RegularExpression(#"^[0-9]{4,}$", ErrorMessage = "Minimum 4 numbers required & contain only numbers")]
public string lst_fur_tel { get; set; }
[Required(ErrorMessage = "E-mail is required!")]
[RegularExpression("^[a-zA-Z0-9_\\.-]+#([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$", ErrorMessage = "E-mail is not valid")]
[Display(Name = "Email")]
public string email { get; set; }
[Required(ErrorMessage = "A reason is required!")]
[Display(Name = "Reason")]
public string reasn { get; set; }
public string tele { get; set; }
Also, how do I generate a number automatically for the "Cust_Id" like a database do with the sql code IDENTITY or computed.
You have 2 problems. First, this line is wrong:
var customer = new {fname = cust.fname,lname = cust.lname, tele = cust.tele, email = cust.email, reasn = cust.reasn };
You are creating an anonymous type instead of a customer object. Try this instead:
var customer = new customer
{
fname = cust.fname,
lname = cust.lname,
tele = cust.tele,
email = cust.email,
reasn = cust.reasn
};
Secondly your context DefaultConnection is wrong and contains this:
public DbSet<DefaultConnection> customers { get; set; }
You are creating a DbSet of your context class instead of customers. This should be:
public DbSet<customer> customers { get; set; }
You cannot add Anonymous typed class or Dynamic to a DbSet so you need to create an instance class of customer in order to be added to your DbSet.
public ActionResult Contact(Customer cust)
{
try
{
if (ModelState.IsValid)
{
cust.Tele_comp();
saveIntoDb(cust); // database
SendMail(cust); // mail sender
return RedirectToAction("Submited", "Home");
}
return null;
}
catch (DataException)
{
return View(cust);
}
}
private void saveIntoDb(Customer cust)
{
using (var cust_In = new DbContext())
{
var customer = new Customer {fname = cust.fname,lname = cust.lname, tele = cust.tele, email = cust.email, reasn = cust.reasn };
cust_In.Customers.Add(customer); //HERE IS THE ERROR!!!
cust_In.SaveChanges();
}
}
Also your DbContext.cs class should have this instead of your code:
public DbSet<Customer> Customers { get; set; }
For the generation of primary key you should use this:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
Make sure you try this tutorial first:
http://msdn.microsoft.com/en-us/data/jj572366.aspx

Using Validator.TryValidateObject in ASP.NET MVC

Validator.TryValidateObject everytime return true. Why? I tried different field values.
Its my model:
public class CompanyPreviewMeta
{
[Required]
[Display(Name="Изображение")]
public string Image { get; set; }
[Required]
[Display(Name="Текст")]
[StringLength(100, MinimumLength = 20, ErrorMessage = "Значение {0} должно содержать от {2} до 100 символов.")]
public string Text { get; set; }
}
It's my validation, and "valid" is true everytime:
ValidationContext ValidatorContext = new ValidationContext(model, null, null);
List<ValidationResult> result = new List<ValidationResult>();
bool valid = Validator.TryValidateObject(model, ValidatorContext, result, true);

Categories

Resources