I'm writing code in ASP.NET but it's not a language related question. Every time when I write a web application and I display some data from database using razor view I have this problem. For example I have simple table with Id and Name in my database. I want to display a table with Names using EditorFor fields. User can edit all the data and save it. So I use row Id as EditorFor Id, read input values in javascript method, use WebMethod to pass them to Controller and save changes to database. But in this case I can change EditorFor Id in Firebug and pass changes with wrong ids. What's the way to edit data in that case? I don't want to click edit link and redirect user to edit page when he can edit one row. I have 5 rows in database with Names and I want to edit all of them at once.
That's some kind of security, and you must add security method in this situation.
One way is to use RBAC method for your security structure. For example:
Create a table and name it user_groups, then create table of users that has and foreign key to user_groups
Then also add the foreign key to your " simple table "(that has id and name) to user_groups, that represent witch user_groups can update the row,
I this that's clear. When someone want to edit a row, you check if that user has permission to change the row or not?
You can search term "Role Base Access Control" in asp mvc,
Also something useful here:
http://www.webdevbros.net/2009/12/16/role-based-access-control-in-asp-net-mvc/
[HttpPost]
public ActionResult EditEmailTemplate(EmailTemplate_Mst emailTemplate_Mst, string Command, int id = 0)
{
try
{
EmailTemplate_Mst et = _repository.GetEmailById(id);
if (Command == "Update")
{
et.Title = emailTemplate_Mst.Title;
et.EmailTemplate_Content = emailTemplate_Mst.EmailTemplate_Content;
et.EmailTemplate_LastModifyBy = Convert.ToInt64(Session["UserId"].ToString());
et.EmailTemplate_LastModifyDate = DateTime.Now;
_repository.UpdateEmail(et);
return RedirectToAction("ViewEmailTemplate");
}
}
catch (Exception)
{
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(new { id = emailTemplate_Mst.EmailTemplate_Id });
}
http://www.c-sharpcorner.com/UploadFile/4d9083/creating-insert-update-and-delete-application-in-mvc-4-using/
Related
To give some context on what I know. I learn some time ago from this tutorial even when you set a field from your model READ ONLY on the View how easy is use tools like fiddler to alter the POST data and change the value.
What you do in this case is use Model Binding to specify the list of fields you want Edit and ignore the rest
[HttpPost]
[ActionName("Edit")]
public ActionResult Edit_Post(int id)
{
EmployeeBusinessLayer employeeBusinessLayer = new EmployeeBusinessLayer();
Employee employee = employeeBusinessLayer.Employees.Single(x => x.ID == id);
UpdateModel(employee, new string[] { "ID", "Gender", "City", "DateOfBirth" });
^^^ Name isnt updated
if (ModelState.IsValid)
{
employeeBusinessLayer.SaveEmployee(employee);
return RedirectToAction("Index");
}
return View(employee);
}
Now I learn about Authentication using AspNet.Identity.
Can check if user isn't authenticated to send him to the Login page.
If user belong to certain Role, I can show personalized menus.
Or check the list of contacts for the user and show only those.
But even if you filter the list of contacts based on the user_id before create the view you can access the actions EDIT and DELETE using links like this.
http://MVCDemo/Employee/Edit/1
http://MVCDemo/Employee/Delete/1
And as long you are authenticated, you can change other users data.
So, how you integrate authentication with CRUD actions to avoid unintended actions?
Seem to me to solve this situation I have to apply something similar to the Model Binding example. When get and Edit or Delete post request, first get the Employee from the DB using the id and compare if belong to the current user.
But that create a lot of repeats code across all the functions.
So what is the right way to handle this?
EDIT
To try to make it clear. Lets assume I have an address book application. Each user have contacts:
contact_id user_id name phone ....
1 1 A
2 1 B
3 1 C
4 2 D
5 2 E
6 2 F
The action to show contacts use authenticated user_id to filter the contacts before send it to the view.
Then you have an Edit/Delete actions where you get contact_id and process to Update/Delete. Even when both users have permission to these actions they shouldn't be able to affect other users contacts. But as I explain is very easy change the page using a tool like fiddler.
I can only speak from personal experience but I don't know any MVC features for managing this for you; I've only ever taken the approach you outline of retrieving the data model from the database and explicitly writing the logic to check if the current user has permissions to modify this resource, returning a 401 error if not.
You can compare the incoming request userid with the userid you have in the session, encrypted cookie or web tokens like jwt which ever you use.
Usually if i have load balancer i use encrypted cookie where i store the logged in user's id and compare it with the id i am getting from the request. if both match, update/delete profile else return Unauthorized.
Same can be done in case of Session or tokens as well.
I am developing as asp.net MVC application and I have two routes first is
http://localhost:60184/Owner/Edit/11 which belongs to user-1 and the second is http://localhost:60184/Owner/Edit/1 to user-2. I want both users to edit, delete, update only his/her own data and for that I have use [Authorize(Roles = "Owner")] but both owners can see each other data by only changing the id value in the route?
Any one please help how to solve this?
You just need to add OwnerId field to your database table that you want to edit and compare it with the current user ID.
public ActionResult Edit(SomeVmClass model)
{
var entity = //fetch the entity from DB
if(User.Identity.GetUserId() != entity.OwnerId)
return HttpNotFound();
// your code
}
A friend asked me to use Output Cache to cache data, that works good, but the thing is that I want to get data from the database and then interact with that data, I mean, I want to grab a huge data from the database, and then get only some data from that huge data according to the user activity dynamically.
[OutputCache(Duration =600, VaryByParam ="none")]
public ActionResult GetData()
{
var result = context.People.ToList();
return View(result);
}
That's an example, but let's say that I want to use pagination for that data in my view, and I want to show every person according to the date birth, for example default page current week, with a link called 'Next' and other one called 'Previous' . When 'Next' link is clicked I want to display only the people which birthday is the next week, same thing with 'Previous', showing just people with birthday on the previous week..
But using Output Cache I'm displaying the same data all the time... Is it possible to do this interacting with the database only the first time, caching that data, and then interacting with that data and not read against the database again during the time I set on the duration?? Do you advise me to use another Caching Tool different than Output Cache?
Instead of caching the entire view, maybe you could just add the result to the session state?
public ActionResult GetData()
{
if (HttpContext.Current.Session["peopleList"] != null)
{
return View((List<People>)HttpContext.Current.Session["peopleList"]);
}
else
{
var result = context.People.ToList();
HttpContext.Current.Session["peopleList"] = result;
return View(result);
}
}
You can control how long session state lasts in your Web.config or through IIS.
I've got a problem with adding data to my database. I have table for book, users and borrows. So, when user is logged in application and pick book, I want to add new data to borrows table. I thought it should be something like this(I call this method where click on chosen book):
public ActionResult AddIt([Bind(Include = "BorrowId,BorrowDate,GiveBackDate,BookId,UserId")] Borrows br))
{
br.BorrowDate = DateTime.Now;
br.BookId = int.Parse(Session["BookId"].ToString());
br.UserId = int.Parse(Session["UserId"].ToString());
if (ModelState.IsValid)
{
db.borrows.Add(br);
db.SaveChanges();
return RedirectToAction("Index");
}
return RedirectToAction("Index");
}
Getting UserId and BookId works, but there is something wrong while call this method. I got "Can't find resource" error. So, how should look that method? GiveBackDate is Nullable and I want it to be null when create record.
EDIT: I rebuilded project and that error disapeared.. But code still doesn't work properly. Seems like asp did not count BorrowIds. If database is empty I can save one record. But if one record is saved, next one goes with id 0 and generates error:
Violation of PRIMARY KEY constraint 'PK_BORROWS'. Cannot insert duplicate key in object 'dbo.Borrows'. The duplicate key value is (0).
The statement has been terminated.
Error at 84 row:
84: {
85: db.Borrows.Add(br);
86: db.SaveChanges();
87: return RedirectToAction("Index");
88: }
I call AddIt here, in BookController:
public ActionResult borrow(int id)
{
return RedirectToAction("AddIt", "Borrows");
}
Make sure the URL is correct. Controller, action and routing-wise
Make sure you're actually making a HTTP Post, since the action is mapped to a Post verb only.
I found a reason of violation primary key error. When I was creating database, I didn't add an IDENTITY property to my primary key BorrowID column. So, id should look like this:
CREATE TABLE [dbo].[Borrows] (
[BorrowId] INT IDENTITY (1, 1) NOT NULL,
How can I get the current username of whoever made changes on a table? A button?
private string GetCurrentUserName()
{
return Utilities.CurrentUsername();
}
UTILITIES:
public static string CurrentUsername()
{
string[] data = HttpContext.Current.User.Identity.Name.ToString().Split(new char[] { '\\' });
string retVal = data[1];
return retVal;
}
If you are referring to changes on a database table, you will need to track the user who last updated each record in the table itself. I typically use an update_user and update_dt column in every table for his purpose. I make sure every update, insert, and delete update this columns with the user and date/time of the update. You can also have a pair of create_user and create_dt columns to maintain row creation.
I think you need to clarify what you mean in your question however. Getting the last update user of a button doesn't make any sense, unless you store each time the button is clicked somewhere.