This is maybe a little bit of an open ended question.
Basically, I have an MVC project, people can log in and create a profile. If they are logged in and on their profile page they display Edit buttons to enable them to be able to edit sections of their profile.
This is done via an attribute in the View Model that checks if the profile username matches the logged in username.
What I'm having a little trouble with is when posting back to update details via ajax, how can I ensure the user is updating his profile and no one else's. I obviously want to make sure no one can hack the site and update someone else's profile.
Do I just have a check in the action that checks if the logged in user matches the profile they are on... if it doesn't then return a 401? Is there a sexier way of doing this? Custom Attribute etc...?
First of all, you should use the AuthorizeAttribute (or a subclass of it) to protect your controller actions from unauthorized access.
[HttpPost]
[Authorize]
public ActionResult Edit(Model model)
{
// edit model here
}
Secondly, to ensure that the user doesn't update somebody else's profile, you should design your action method to use the current user instead of putting it into the URL.
[HttpPost]
[Authorize]
public ActionResult Edit(Model model)
{
// Only allow the logged in user to edit their own profile
// TODO: Update the user's database record by using the user variable as the key.
var user = User.Identity.Name;
// edit model here
}
Third, you should use the ValidateAntiForgeryToken in conjunction with the AntiForgeryToken HTML helper to ensure your pages are posted to the server by the same browser that requested them.
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult Edit(Model model)
{
// Only allow the logged in user to edit their own profile
// TODO: Update the user's database record by using the user variable as the key.
var user = User.Identity.Name;
// edit model here
}
#using (Html.BeginForm("Edit", "Account")) {
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Change Password Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.OldPassword)
#Html.PasswordFor(m => m.OldPassword)
</li>
<li>
#Html.LabelFor(m => m.NewPassword)
#Html.PasswordFor(m => m.NewPassword)
</li>
<li>
#Html.LabelFor(m => m.ConfirmPassword)
#Html.PasswordFor(m => m.ConfirmPassword)
</li>
</ol>
<input type="submit" value="Change password" />
</fieldset>
}
Another thing you could do is to add a second identifier to the user table that is not available outside of the application and require that any post that edits data have a one-way hash of this second identifier in it or it will be rejected. This identifier should of course be unique per user (best to do a Guid) and then use a one-way hash algorithm followed by URL encoding.
/Account/Edit/?hash=AKXHAonyrOtruBO%2FVI%2FGr%2FM%2B4ZadjrS3YRt21ILSLntssu23l%2FN6hpUilZM8Hkgn%2Bg%3D%3D
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult Edit(Model model, string hash)
{
// Only allow the logged in user to edit their own profile
// TODO: Update the user's database record by using the user variable as the key.
var user = User.Identity.Name;
// After looking up the user, hash the hidden ID field using the same algorithm
// and ensure that the hashes match. If not, throw a 401 error.
// edit model here
}
This may help if you have a record with a sequential ID in the URL and you don't want the user to just change the ID to some other value and post it.
It helps to analyze the Account controller that is generated when you make a brand new MVC project from the VS template to see how it is put together.
Related
In my application I want to show user profile image in layout page (_LoginPartial).
In AspNet Identity membership their is a AspNerUser table . I want to customize this AspNerUser table to maintain image field.
then show that image in Layout page (_LoginPartial) view.
How can I do this ? Really appreciate can suggest a way to do this
EDIT
I generated my DataBaseContext name using ADO.NET entity model , database Context name is ProjectNameEntities
then I tried to enable migration using following command on PMC
Enable-Migrations -ContextTypeName myProject.Models.ProjectNameEntities
but then I'm getting following error
Creating a DbModelBuilder or writing the EDMX from a DbContext created
using Database First or Model First is not supported. EDMX can only be
obtained from a Code First DbContext created without using an existing
DbCompiledModel.
is this possible to do with edmx model ?
Add Field Model (Code First)
First thing you need to do is modify the ApplicationUser Model that the database table is built from. This class is usually located in the IdentityModels.cs file. Add a new field to hold the image:
public class ApplicationUser : IdentityUser
{
// maybe be other code in this model
public byte[] ProfilePicture { get; set; }
}
Next you need to update your database to reflect the changes (assuming you are using Code First). You can find detailed information on the process in this article.
Enable-Migration
Add-Migration "Added user profile"
Update-Database (will apply any pending migrations to the database)
Return profile picture
Now add an action to a controller similar to:
public FileContentResult Photo(string userId)
{
// get EF Database (maybe different way in your applicaiton)
var db = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
// find the user. I am skipping validations and other checks.
var user = db.Users.Where(x => x.Id == userId).FirstOrDefault();
return new FileContentResult(user.ProfilePicture, "image/jpeg");
}
Finally in your _LoginPartial add the following call to the Action we just created where ever you want the image to show up. You will need to change the Controller name to what ever controller you put your action on.
<img src="#Url.Action("Photo", "Account" , new { UserId=User.Identity.GetUserId() })" />
Save profile picture
First you need to create a page to upload the image. Create an action to return the form:
[HttpGet]
public ActionResult Profile()
{
ViewBag.Message = "Update your profile";
return View();
}
The Razor view would be called Profile.cshtml and look have a form on it that looks like: (note that the Action and controller location may be different for you depending on how you structure your project)
#using (Html.BeginForm("Profile", "Manage", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<fieldset>
<legend>Photo</legend>
<div class="editor-label">
<label for="profile">FileName:</label>
</div>
<div class="editor-field">
<input name="Profile" id="profile" type="file" />
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
The form will post back to an action so you need to create one that looks like:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Profile(HttpPostedFileBase Profile)
{
// get EF Database (maybe different way in your applicaiton)
var db = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
// find the user. I am skipping validations and other checks.
var userid = User.Identity.GetUserId();
var user = db.Users.Where(x => x.Id == userid).FirstOrDefault();
// convert image stream to byte array
byte[] image = new byte[Profile.ContentLength];
Profile.InputStream.Read(image, 0, Convert.ToInt32(Profile.ContentLength));
user.ProfilePicture = image;
// save changes to database
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
Note that there needs to be validations and checks put in place according to your rules but this is the basic idea on how it works.
Created a GitHub project that shows the basics above in a working sample: https://github.com/jsturtevant/mvc-aspnet-identity2-profile-picture-sample
I am a newbie and is making web application in Visual Studio 2010 using MVC2 + Entity framework.
I have a situation in which I want to put both operations i.e create user / update user at same view, I have also tried attaching relevant picture where I have made two portions one for create user and second for manage users.
My 'create user' fields are at top of website and when user click 'create button' page got refreshed and all enlisted users gets displayed on same view under second portion 'manage users' showing link to edit/delete them.
I want that when I click on edit link, that particular entity fields get populated on same view in first portion 'create user' where I can modify them and press 'update button'
VIEW
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Admin.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%# Import Namespace="MyNamespace" %>
<h4>Create New User</h4>
<form method="post" action="/Lignum/CUser">
<label for="inputEmail3">Full Name</label>
<input type="text" name="Fullname" id="txtFullname" >
<label for="inputEmail3">Email</label>
<input type="email" name="Email" id="Email1">
<button id="btnCUser" class="btn btn-primary">Create</button>
</form>
<h4>Manage Users</h4>
<table>
<tr>
<td>Sr#</td><td>Name</td><td>Email</td><td></td>
</tr>
<% int i=0;
foreach (MyWebsite.Models.User objUser in ViewData.Model as IEnumerable<MyWebsite.Models.User>)
{%>
<tr>
<td><%= ++i%></td>
<td><%= objUser.Fullname%></td>
<td><%= objUser.Email%></td>
<td>
Edit
</td>
</tr>
<%}%>
</table>
CONTROLLER
public ActionResult Index()
{
return View("UserMgt", _repositoryUser.SelectAll());
}
public ActionResult Edit(object Id)
{
if (Id != null && Id.ToString().Trim().Length > 0)
{
int param = int.Parse(Id.ToString());
return View("UserMgt", _repositoryUser.SelectByID(Id));
}
return View("404");
}
You will need to make use of JQuery & Ajax to achieve this. Your page is getting refreshed most likely because your are submitting a form. Instead of form submit, you need to attach a function to handle onclick event.
In that function you will know which item is clicked, load the data to be edited from the server sending an ajax request with item id.
When request return you can then open a JQuery popup window or update page's html to display data. User will be allowed to make changes and on Ok button click you can again send the data back to server to save.
I am looking for an example online to refer to you as my code is little complex. You can also look for an example online.
UPDATE:
i want that when i click on edit link, that particular entity fields
get populated on same view in 1st portion 'create user' where i can
modify them and press 'update button'
Ok, looked at your code. As I said earlier you will need to define an "id" for each html element, the value will be objUser.UserId (you can prefix something if you want). Now define a click event for all html elements i.e. .
For a working example refer this link.
I suggest you progress as you gain some insight and post updated code. We will suggest what's needed for next step. This way you would learn more.
You can try following:
Create a View model with whatever you need on the create page i.e. user details
strongly bind your view with this view model
Have three action methods in controller "Create","Populate" and "Update" with Update and Populate taking Id of the entity as input (you can choose better names)
Initially call Create method which will just return an empty view model with your View
Have a hidden variable in view which will store the Id of the entity (in case of create this will be zero)
on click of create just take the value of this hidden variable and do a post to Update action method.In this case if it is new entity id will be zero
On click of edit go call Populate method with id of the entity which again will return ViewModel with entity details loaded to the same create view (also set the hidden variable with id)
In your update method based on the id perform create or update operation i.e. Create for zero and Update for 1
If you post your code or other details I can give some more details using code.
EDIT: OK few more details in terms of code.
//This is the view model you need to bind to your view
public class UserViewModel
{
public int UserId { get; set; }
public string Email { get; set; }
public string FullName { get; set; }
public List<Users> UserList {get;set;} //For binding to the grid
}
Below are the action methods in controller.
public ActionResult Create()
{
var viewModel = new UserViewModel();
//Logic: Create empty view model for create
return View("UserMgt", viewModel);
}
public ActionResult Edit(int id)
{
var viewModel = new UserViewModel();
//Logic: populate the view model based on the id
return View("UserMgt", viewModel);
}
// Call this method using Jquery ajax
public bool Update(UserViewModel user)
{
if (user.Id == 0)
//Logic : Create the user
else
//Logic : Edit the user
return Json(status); //Status = true if successful else false
}
Initially call create.On click of edit call Edit method.On click of save call Update.
For using jquery ajax follow below link
http://api.jquery.com/jquery.ajax/
I am working on an MVC 5 application and have the following scenario.
The user clicks my registration link to create an account and is served with a view like so. Works as expected.
[HttpGet]
[AllowAnonymous]
public ActionResult RenderRegisterModal(string userType)
{
IList<UserTypesDto> userTypes = _userService.GetUserTypes();
var usersRegisterUserViewModel = new UsersRegisterUserViewModel {UserTypes = new List<SelectListItem>()};
usersRegisterUserViewModel.UserTypes.Add(new SelectListItem
{
Text = "Select a registration type",
Value = "0"
});
foreach (UserTypesDto userTypeDto in userTypes)
{
usersRegisterUserViewModel.UserTypes.Add(new SelectListItem
{
Text = userTypeDto.UserType,
Value = userTypeDto.UserTypeId.ToString()
});
}
return PartialView("_RegisterModal", usersRegisterUserViewModel);
}
Now based on the type of customer / userType they choose I need to present different views. Should not have any issue getting the view into the modal as it is ajax... So lets call this a pseudo wizard to collect data. The issue I am having and the basis for this probably simple question, that I am thinking too much about, is how do I save the data from each step?? Temp Table? InMemory Cache using the session id as the key? Cookies? "Gross"
The ActionMethod that the post goes to looks like this.
[AllowAnonymous]
[HttpPost]
public ActionResult Register(UsersRegisterUserViewModel usersRegisterUserViewModel)
{
//TODO Return a view for the next step based on the CustomerType contained in the viewModel
return View();
}
Any feedback would be greatly appreciated.
OPTION 1:
The easiest way, is that the NEXT view have a set of hidden fields with the information entered in the previous view and which was posted to the Action.
This way, once you post the second view, you will be posting all the information (previous one and the one you entered in the second view).
OPTION 2:
If you are not comfortable with the first approach, you can have several PartialViews that are shown or hidden in javascript on UserType combo changes. Your ViewModel should have all the propertied you need to hold the information before posting back to server.
This option came in 2 flavors: you render all your usertype partials at the begining (so you need to hide them at first), or you can get the partial via Ajax once the user selected one user type.
OPTION 3:
You can use Session to hold the sensitive data from the register form, redirect to the next view depending on user type, post the new form, and in the Action you retrieve the information from Session... and with all the information in your hands store it in database.
If you're using razor, persist the different parts of your model like this
#Html.EditorFor(model => model.data1)
#Html.EditorFor(model => model.data2) #*Wizard part 1*#
then in the next view for your wizard
#Html.HiddenFor(model => model.data1)
#Html.HiddenFor(model => model.data2)
#Html.EditorFor(model => model.data3)
#Html.EditorFor(model => model.data4) #*Wizard part 2*#
and then
#Html.HiddenFor(model => model.data1)
#Html.HiddenFor(model => model.data2)
#Html.HiddenFor(model => model.data3)
#Html.HiddenFor(model => model.data4)
#Html.EditorFor(model => model.data5) #*Wizard part 3...*#
and so on...
I'm creating some user profile edit forms in MVC4 at the moment and for testing I was rendering the UserId property into a readonly textbox on the form like this:
<li>
#Html.LabelFor(model => model.UserId)
#Html.TextBoxFor(model => model.UserId, new { #readonly="readonly"})
</li>
As I'm nearing completion of the edit form I removed this textbox as it's just using up real estate. Once I had done this the model sent back to the controller when saving had the integer default value of 0 and then the Entity Framework blows up as it cannot update any rows. So I added this to the form:
<li>
#Html.HiddenFor(model => model.UserId, new { #readonly="readonly"})
</li>
Is this a safe move? Should I be using the ViewBag for things like this? On the profile details page I render an edit button like this:
#Html.ActionLink("Edit", "Edit", new { id=Model.UserId })
Meaning that the UserId is rendered in the link. Is this safe and secure or do I need to rethink how I move the models and ids around the UI?
TIA,
Is this a safe move?
This will do the job of sending the id to the server. Just get rid of the readonly="readonly" attribute which makes very little sense for a hidden input.
Should I be using the ViewBag for things like this?
This doesn't change anything in terms of security. Any user could still put whatever id he wants. Whether you are using a hidden field or an ActionLink you are still sending the id as plain text to the server and anyone could forge a request and put whatever id he wants. So if you site uses some form of authentication you must absolutely check on the server side that the id that you received actually is a resource that belongs to the currently authenticated user before attempting to perform any actions on it. Otherwise some authenticated user could supply the id of a resource that belongs to another user and be able to update it. Of course that's just a hypothetical scenario, it's not clear at all if this is your case and whether this id needs to be secured.
If UserId is sensitive, then there are other options
Keep UserId server side only with Session state (if your architecture allows for Session)
Put it in an encrypted cookie. Note as per Darin, that these can be compromised.
If it isn't sensitive, then your HiddenFor is fine - post it back with the rest of the form.
Don't put it in your ActionLink Querystring unless this is part of your route (i.e. /Controller/Action/id)
I would strongly suggest using ValueInjecter. Here is a code snippet doing the same thing
[HttpGet]
public new ActionResult Profile()
{
var model = new ProfileModel();
model.InjectFrom<UnflatLoopValueInjection>(this.GetCurrentUser());
return View(model);
}
[HttpPost]
public new ActionResult Profile(ProfileModel model)
{
if (ModelState.IsValid)
{
this.GetCurrentUser().InjectFrom<UnflatLoopValueInjection>(model);
try
{
_userService.SaveOrUpdate(this.GetCurrentUser());
TempData["Success"] = "User was successfully updated.";
return RedirectToAction("Profile");
}
catch (Exception)
{
ModelState.AddModelError("Exception", "Unexpected error");
}
}
return View(model);
}
And here is the view...
#using (Html.BeginForm("Profile", "Account", FormMethod.Post, new { #class = "form-horizontal" }))
{
#Html.ValidationSummary(true, "Unable to update profile. Please correct the errors and try again.", new { #class = "alert alert-block alert-error" })
#Html.EditorForModel()
<div class="form-actions">
<input type="submit" value="Update" class="btn btn-primary" />
</div>
}
i want to show/hide certain parts of a View based on Authentication-status or Roles. For my controller actions I have extended ActionFilterAttribute so I can attribute certain Actions.
<RequiresRole(Role:="Admin")> _
Function Action() as ActionResult
Return View()
End Function
Is there a similar way (attributing) which I can use in the Views? (so not like this: How can I create a view that has different displays according to the role the user is in?)
You can access the user's logged-in roles from the view like this:
<% if (Page.User.IsInRole("Admin")) { %>
<td>
<%= Html.DeleteButton("delete", model.ID) %>
</td>
<% } %>
and maybe your extension method with something like:
public static string DeleteButton(this HtmlHelper html,
string linkText, int id)
{
return html.RouteLink(linkText,
new { ID = id, action = "Delete" },
new { onclick = "$.delete(this.href, deleteCompleted()); return false;" });
}
Obviously, I'm using JavaScript to perform an HTTP DELETE to my controller action, to prevent page crawlers from accidentally deleting data from getting my pages. In my case I'm extending JQuery with a delete() method to supplement the HTTP verb.
I new this existed, but took a while to find. Here's what I am using:
<asp:LoginView runat="server">
<AnonymousTemplate>
You are not logged in yet. Please log in.
</AnonymousTemplate>
<RoleGroups>
<asp:RoleGroup Roles="Admin">
<ContentTemplate>
You are an Admin.
</ContentTemplate>
</asp:RoleGroup>
<asp:RoleGroup Roles="Customers">
<ContentTemplate>
You are a customer.
</ContentTemplate>
</asp:RoleGroup>
</RoleGroups>
<LoggedInTemplate>
Simple Log in check
</LoggedInTemplate>
</asp:LoginView>
This allows you to show different content to different users based on their login state or credentials.