Model binder to decrypt string and convert to int - c#

I have a model which has a property id of type int.
I pass the id in the url like Detail/20 for fetching the data. But, now my customer says they don't want to see the id, since any one can modify and see other records.
Now, I've decided to encrypt and decrypt it, and assign it to another property: encId.
public ActionResult List()
{
foreach(Employee e in empList)
{
e.encId = MyUtil.Encrypt(id,"sessionid");
}
return View(empList);
}
Finally, I make my url like Detail/WOgV16ZKsShQY4nF3REcNQ==/.
Now, all I need is to decrypt it back to the original form and assign it to the property id of type int.
public ActionResult Detail(int id) //don't want (string id)
{
}
How can I write my model binder that decrypt and convert it to valid id? Also if any error/exception occurs, it has to redirect to 404 Error page. It might happen when user manually edits some useless text in the url (encrypted id).

First, this is not the way to go about securing your website and data. Please take a look at the issues with Security Through Obscurity. You would be better off defining sets of permissions on each employee record and who can or cannot edit them. Such an example could look like this:
public ActionResult Detail(int id)
{
if(MySecurityProvider.CanView(id, HttpContext.Current.User.Identity.Name){
return View();
}
Return RedirectToAction("PermissionIssue", "Errors");
}
With that said, to continue on the path you are on, simply do the decryption within the action result.
public ActionResult Detail(string Id)
{
int actualId;
try{
actualId = MyUtil.Decrypt(id);
}catch(Exception e){
//someone mucked with my encryption string
RedirectToAction("SomeError", "Errors");
}
var employee = MyEmployeeService.GetEmployeeById(actualId);
if(employee == null){
//This was a bad id
RedirectToAction("NotFound", "Errors");
}
Return View(employee);
}

Related

Is it possible to have a generic [Bind] in c# methods?

what I try to do is to bind every incomming value from my response to a string or stringlist dynamicly / generic.
So assume I would know each POST-Value of my request, e.g.
string1 = Test
string2 = Test2
I would write:
[HttpPost]
public ActionResult DoFoo(string string1, string string2)
{
}
or
[HttpPost]
public ActionResult DoFoo(string string1, [Bind(Prefix = "string2")string myString2)
{
}
My situation know is, that I have X strings with my post request. So I dont know the exact number nor the names to catch in my backend.
How to catch every given Post-value without knowing this / how to catch the values dynamicly?
I don't feel that why you have to use Prefix with BIND, when you have to bind every incoming field of response. Bind is not a good choice for that. You can use bind if you have multiple entities at the same time. Reference here
that I have X strings with my post request.
If you have to use all the fields then you can use FormCollection or Model object to receive those fields. FormCollection automatically receive all the fields from view and bind them to a collection. See this for proper example. And a code snippet is below for reference.
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
Student student = new Student();
student.FirstName = collection["FirstName"];
student.LastName = collection["LastName"];
DateTime suppliedDate;
DateTime.TryParse(collection["DOB"], out suppliedDate);
student.DOB = suppliedDate;
student.FathersName = collection["FathersName"];
student.MothersName = collection["MothersName"];
studentsList.Add(student);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
However if you have to deal with only one particular field/set of fields then you can use either Include or Exclude as per your convenience with BIND. Example shown here and code snipped is added below.
In following way you are telling that you only want to include "FirstName" of User model while receiving the form content. Everything else will be discarded.
[HttpPost]
public ViewResult Edit([Bind(Include = "FirstName")] User user)
{
// ...
}
And in following example you are telling that, please exclude "IsAdmin" field while receiving the fields. In this case, value of IsAdmin will be NULL, irrespective of any data entered/modified by end-user in view. However, in this way, except IsAdmin, data rest of the fields will be available with user object.
[HttpPost]
public ViewResult Edit([Bind(Exclude = "IsAdmin")] User user)
{
// ...
}

Is it possible to RedirectToAction passing data without using parameters in an ASP.NET MVC project?

In my controller of an ASP.NET MVC project, I have a
return RedirectToAction("CreatePerson", "Home")
This View is a form that creates a person and that works fine. However, I want to RedirectToAction and pre-fill the form with data collected from a form that creates a User for the system.
How would I pass the data from the CreateUser form in the CreatePerson form?
I know that I could use parameters, but would this really be the best method if most of the time I am calling the CreatePerson view without needing those parameters.
Any help in the right direction would be greatly appreciated.
You can't send data with a RedirectAction. That's because you're doing a 301 redirection and that goes back to the client.
So better use TempData
Assuming you will have model to createperson with following properties:
public class CreatePersonData
{
public string name {get; set;}
public string address {get; set;}
}
Now fill the model and store in TempData
CreatePersonData person=new CreatePersonData();
person.name="SomeName";
person.address="SomeAddress";
TempData["person"]=person;
return RedirectToAction("CreatePerson", "Home")
Now while receiving just receive it from tempdata and pass the filled model to the view
public ActionResult CreatePerson()
{
CreatePersonData person=new CreatePersonData()
var loadPerson= TempData["person"];
person = loadPerson;
return View(person);
}
UPDATE
As #StephenMuecke made a point of loosing data with TempData you might need to use .Keep or .Peek with TempData to retain the value for future requests
Ex:
with .Peek
//PEEK value so it is not deleted at the end of the request
var loadPerson= TempData.Peek("person");
or with .Keep
//get value marking it from deletion
var loadPerson = TempData["person"];
//later on decide to keep it
TempData.Keep("person");
or as #Stephen said just pass the id and select the user from database
Ex:
return RedirectToAction("CreatePerson", "Home", new { ID = User.ID });
Now in your CreatePerson ActionResult just get it from db as below:
public ActionResult CreatePerson(int ID)
{
CreatePersonData person=new CreatePersonData();
var user=(from u in tbl_user select u where u.ID=ID);
person.name=user.name;
person.address=user.address;
return View(person);
}
UPDATE 2
You can combine both of the above approaches like storing data in TempData and passing the ID with routeValues and check if TempData isn't null then fallback to retrieval of data using ID approach.
Ex:
public class CreatePersonData
{
public string Id{get; set;}
public string name {get; set;}
public string address {get; set;}
}
public ActionResult CreatePerson(int ID)
{
CreatePersonData person=new CreatePersonData();
var loadPerson=(CreatePersonData)TempData.Peek("person"); //cast the object from TempData
if(loadPerson!=null && loadPerson.Id==ID)
{
person=loadPerson;
}
else
{
var user=(from u in tbl_user select u where u.ID=ID);
person.name=user.name;
person.address=user.address;
}
return View(person);
}
Many Overloads exist for this
protected internal RedirectToRouteResult RedirectToAction(string actionName, object routeValues);
What you want to do is right click on that Method and View Definition, you should see many overloads for it
// Summary:
// Redirects to the specified action using the action name and route values.
//
// Parameters:
// actionName:
// The name of the action.
//
// routeValues:
// The parameters for a route.
//
// Returns:
// The redirect result object.
1- create an object of your model
2- you can use session to pass parameter to your other action,
3- then put the object in a viewbag agg get it in your view

The model item passed into the dictionary is of type 'System.Int32', but this dictionary requires a model item of type 'IMS.Models.Dealer'

I know there are a lot of similar questions here, but none of them could solve my problem.
When I access the URL: http://localhost:42626/dealer/edit/2
Error occurs:
The model item passed into the dictionary is of type 'System.Int32',
but this dictionary requires a model item of type 'IMS.Models.Dealer'.
DealerController Code:
[HttpGet]
public ActionResult Edit(int DealerId = 0)
{
//get from database
Models.Dealer dealer = new Models.Dealer();
string Error;
if(dealer.GetDealer(DealerId, out Error))
return View(dealer);
else
{
RedirectToAction("Index");
}
return View(DealerId);
}
[HttpPost]
public ActionResult Edit(Models.Dealer dealer)
{
//if All validations are true
if (ModelState.IsValid)
{
string Error;
//save to database
if(dealer.Save(out Error))
return RedirectToAction("Index");
else
{
TempData["EditMessage"] = "An error occured. Could not update Dealer. Details: " + Error;
return Edit(dealer.Id);
}
}
return Edit(dealer.Id);
}
I've created View with strongly typed Models.Dealer and template is Edit.
If I have defined [HttpGet] and [HttpPost], why is it not accepting int and keep asking for Dealer model??
Currently if the dealer ID can't be found, you're calling RedirectToAction, but ignoring the result and then trying to return your view with the dealer ID. I suspect you want:
[HttpGet]
public ActionResult Edit(int dealerId = 0)
{
//get from database
Models.Dealer dealer = new Models.Dealer();
string error;
if (dealer.GetDealer(dealerId, out error))
{
return View(dealer);
}
else
{
return RedirectToAction("Index");
}
}
I've updated the variable names to be more idiomatic, btw. It feels odd that a dealer itself has GetDealer, mind you - I'd expect some kind of DealerService to be provided to your controller via dependency injection, so you'd then have:
Dealer dealer = dealerService.GetDealer(dealerId);
(I'd also probably use exceptions for error handling rather than strings like this, but that's a different matter.)

ASP.NET MVC: Ignore field(s) when editing

As I'm in the progress of learning ASP.NET MVC, I ran into a question and into some trouble
I'm trying to create a simple blog, just to test out what I have learned so far. But when it comes to editing and leaving a field i run into a problem.
I'm trying to edit an already submitted post on my blog, the post contains few fields: Id, Headline, Message, Author and Date for the submission which should not be edited, just left as it is.
Here is some code:
My post model:
namespace MyBlock.Models
{
public class Post
{
public int Id { get; set; }
[Required]
public string Author { get; set; }
[Required]
public string Headline { get; set; }
[Required]
public string Message { get; set; }
public DateTime Date { get; set; }
}
}
My edit:
[HttpGet]
public ActionResult Edit(int id = 0)
{
Post post = db.Posts.Find(id);
if (post != null) {
return View(post);
}
return HttpNotFound();
}
[HttpPost]
public ActionResult Edit(Post post)
{
if (ModelState.IsValid) {
db.Entry(post).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View(post);
}
And my view for edit:
#model MyBlock.Models.Post
#{
ViewBag.Title = "Edit";
}
<h2>Rediger "#Model.Headline"</h2>
#using (Html.BeginForm()) {
#Html.LabelFor(u => u.Author)
#Html.TextBoxFor(u => u.Author)
#Html.LabelFor(u => u.Headline)
#Html.TextBoxFor(u => u.Headline)
#Html.LabelFor(u => u.Message)
#Html.TextAreaFor(u => u.Message)
<input type="submit" value="Gem" />
}
I know I could throw in a #HiddenFor(u => u.Date) and the same date would be submitted. But I bet there is another way than having it as a hidden field in the source code? I mean this isn't that secure in another example? So I want something else than hidden field here. Can you guys help me out?
If I try to run this as it is. I'm getting an error which is my Date isn't set, which is logic because it want to update that one aswell. But I dont want it to. I want to leave it optional if you could say that.
Don't take candy from strangers
In other words, don't take the information from the client and directly update the DB. You should enforce your business rules on the server side and not trust the client to do it for you.
[HttpPost]
public ActionResult Edit(Post post)
{
if (ModelState.IsValid) {
var dbPost = db.Posts.FirstOrDefault(p => p.Id == post.Id);
if (dbPost == null)
{
return HttpNotFound();
}
dbPost.Author = post.Author;
dbPost.Message = post.Message;
dbPost.Headline = post.Headline;
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View(post);
}
[HttpPost]
public ActionResult Add(Post post)
{
if (ModelState.IsValid) {
var dbPost = db.Create<Post>();
dbPost.Author = post.Author;
dbPost.Message = post.Message;
dbPost.Headline = post.Headline;
dbPost.Date = DateTime.Now(); // Don't trust client to send current date
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View(post);
}
In my own project I enforce rules like this at the domain layer by adding custom validation rules to the ValidateEntity method.
DateTime is a value type, and cannot be null. Thus, it can never be optional.
You need to make a it a nullable type. ie.
public DateTime? Date {get;set;}
In general, most value types in a ViewModel should be nullable, then you use Required attributes to enforce that they contain a value. This allows you to tell whether they failed to enter a value, or whether it's a default value.
In your controller, you can then check if the Date has a value with Date.HasValue and if so, then save the date.
In regards to security, in this case it's not raelly an issue. Assuming someone has access to the page (they pass authorization) and they have the right to update the date, then it doesn't matter if the user can bypass it. All they can do is submit a valid date format. Unless you want to add logic to ensure that the date is within a specific time period, then you don't have to worry. The ModelBinder will not bind to a non-valid date format.
If you want to control whether the user can update the date, say based on role, then you could add logic to your controller to check if the date has a value and the user is in the correct role, otherwise issue an error.
UPDATE:
I think the easiest solution here is to do two things. The first is to make Date nullable, as I mention above. Although this is not strictly necessary if you do not have a form field for Date in your view, if you were to add a form field later then you would get a validation error if you left the textbox empty. I like to prevent future errors from occurring if possible. Also, should someone be posting values to your Edit action manually, and they include a blank Date field, it will fail to validate, rather than simply ignore it. Making the value nullable allows the value to be completely ignored regardless of its value.
Second, is do what #p.s.w.g suggests, and only update the fields that you want updated. Retrieve the post from the database, then update all fields except Id and Date. Then call SaveChanges().
Just my 2cents here. I know this is a simple situation and the answer given is nice and straightforward. But as that list of attributes grows then it could get difficult.
So a different approuch would be along these lines
var t = _db.Blog.Where(x => x.ID == id).FirstOrDefault();
var info = typeof(Blog).GetProperties();
//properties you don't want to update
var properties = info.Where(x => x.Name != "xxx" && x.Name != "xxxx").ToList();
foreach(var p in properties)
{
p.SetValue(t, p.GetValue(temp.Volunteer));
}
_db.Entry(t).State = EntityState.Modified;
_db.SaveChanges();
But if you are just doing a few fields then the above makes sense.
Just use your noggin!

How to write a query to check database value with session value

Can anyone help me? I am taking a session value in variable like
var user = Session["UserName"];
and by this i will get username of an employee who is logged in. Now I want designation of that employee so I wrote
var data=from u in db.EmployeeTabs.Where(p=>p.EmpName==user).Select(v=>v.Designation)
Now variable data will probably contain the designation of employee who is logged in. Now I want a condition, based on the condition I want to redirect the page so I want an if condition like
if(val(data)=="Receptionist")
then it should display a Register.cshtml page, so for that what should I write?
And where to write? Means in an controller? Or i should create some method in controller?
I would recommend reading this tutorial Making some assumptions about your code, I think you want something like
public ActionResult Index()
{
var user = Session["User"];
using (var db = new YourEntity())
{
var data = from u in db.EmployeeTabs.Where(p => p.EmpName == user).Select(v => v.Designation);
if (data == null)
{
return RedirectToAction("Register");
}
Switch(data.First().Designation)
{
case "Receptionist":
return RedirectToAction(Register);
}
}
return View();
}
public public ActionResult Register()
{
return View();
}

Categories

Resources