Passing so far entered data to controller - asp.net

In a form I have different fields (name, age, ...) and the possibility to upload an image. This I want to realize on a different view. The problem is that the data, that is made so far, are not passed to the controller when I want to change the controller. Here a small example of my code:
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Person</legend>
#Html.EditorFor(model => model.Name)
#Html.EditorFor(model => model.Age)
<img src="#Url.Content(Model.ImagePath)" alt="Image" class="imagePreview" />
<li>
#Html.ActionLink("Upload a pic from you!", "UploadImage", new { model = Model }, null)
#* This is the 'problematic' action *#
</li>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Here the method that is calling the upload controller:
public ActionResult UploadImage(Person model)
{
// properties in the passed model are not set
return RedirectToAction("UploadImage", "UploadImage");
}
How it is possible to get the entered information without using the submit button?

Check out this blog post by Bryan Sampica on asynchronus file uploads with MVC. We just used it for a smooth async file upload experience so we didn't have to leave the page. This solves your problem of how to persist the transient data. If your users are using a modern browser (IE 10+, Chrome, FF, etc...) the progress bars actually show file upload progress. It's fairly easy to setup, but, if you follow the instructions to the letter, does require that you add a webAPI controller to your project.

Related

How secure the EntityId in hidden field for Editing Form in Asp.Net Core MVC?

I'd like to create the form for editing some Entity (for example a post) in the database using the Entity Framework Core.
I want to protect the value PostId in the hidden field before rewriting to another value from the browser. I'm wondering about checking the user permissions before updating but I want to create some encryption/signing or something like that.
How can I encrypt or sign the PostId and in the controller decrypt or validate it?
I've created the example form for editing the post like this:
Entity - Post:
public class Post
{
[Key]
public int PostId { get; set; }
[Required]
[StringLength(40)]
public string Title { get; set; }
}
Controller - PostsController with Edit method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("PostId,Title")] Post post)
{
if (ModelState.IsValid)
{
//Update method
}
return View(post);
}
Form for editing:
#model EFGetStarted.AspNetCore.NewDb.Models.Post
#{
ViewBag.Title = "Edit Post";
}
<h2>#ViewData["Title"]</h2>
<form asp-controller="Posts" asp-action="Edit" method="post" asp-antiforgery="true" class="form-horizontal" role="form">
<div class="form-horizontal">
<div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="PostId" type="hidden" />
<div class="form-group">
<label asp-for="Title" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Edit" class="btn btn-default" />
</div>
</div>
</div>
</form>
By encrypting it you don't get any real business value and if the intent is so prevent one user to edit/modify posts he has no access to, you should do it in the backend by following the "Never trust the client" principle and always validate input on the server.
Easiest way to do is to use only the post ID from the model posted in and validate if the user has permissions to modify it. For this the new policy based systems offers resource based permissions which are well documented and can be used to validate the permissions.
Once done, passed take over the values and save the changes.
Also you shouldn't use persistence models inside the views, they easily break your API or your forms when the you change the database layout and navigation properties may cause issues (circular references etc.); especially later on, when lazy loading is implemented (lazy loading can't happen async as its inside a property, so the db call will block the thread).
Take a look at Sergey Akopov's blog post where he proposes a mechanism to deal with this scenario within ASP.NET MVC. His solution is to write a Html Helper that can be called within your view to generate a hidden input to accompany each input that you wish to make "tamper proof". This hidden input contains an encrypted copy of the value that you want to be tamper proof. When the form is posted, the server checks that the posted value and accompanying encrypted value still match - he writes a filter attribute which is applied to the corresponding controller action to perform this check. This adds an extra layer of "never trust the client" security.
Another example here has an interesting discussion (in the comments) around the potential security flaws inherent in this approach - The main one being that a determined attacker could "farm" valid combinations of secure field and encrypted value from their editing sessions, and subsequently use these farmed values to post tampered data with future edits.

ASP.NET MVC On form post, how to get the check status of a checkbox in the view?

In a system where I am using the Identity framework, I have the following form in a view that is bound to the model AppUser:
<div class="form-group">
<label>Name</label>
<p class="form-control-static">#Model.Id</p>
</div>
#using (Html.BeginForm("Edit", "Admin", new { returnUrl = Request.Url.AbsoluteUri }, FormMethod.Post, new { #id = "edituserform" }))
{
#Html.HiddenFor(x => x.Id)
<div class="form-group">
<label>Email</label>
#Html.TextBoxFor(x => x.Email, new { #class = "form-control" })
</div>
<div class="form-group">
<label>Password</label>
<input name="password" type="password" class="form-control" />
</div>
<input type="checkbox" name="userLockout" value="Account Locked" #(Html.Raw(Model.LockedOut ? "checked=\"checked\"" : "")) /> #:Account Locked <br>
<button type="submit" class="btn btn-primary">Save</button>
<button class="btn btn-default"
id="canceleditbutton">
Cancel
</button>
}
The model definition for AppUser:
public class AppUser : IdentityUser
{
public bool LockedOut { get; set; }
/*Other fields not shown for brevity*/
}
My specific question is regarding the checkbox for the LockedOut flag, which is a custom property I added. For a test user, I manually set the flag in the database to True and as expected, on the view the checkbox was checked when it was loaded. Now my goal is to be able to access this in the POST edit method of the AdminController that this form calls on submit. The skeleton for that method is as follows:
[HttpPost]
public async Task<ActionResult> Edit(string id, string email, string password, string userLockout)
{
//Code here to change the LockedOut value in the database based on the input received
}
The issue is that the userLockout parameter comes in as null when I click Save on the submit on the edit screen. The other two values are populated correctly. How can I access the userLockout value, so that I can continue with saving the change into the database if needed?
And lastly, my ultimate goal here is to implement a system where an admin can lock or unlock a user account via the LockedOut flag (which gets checked each time someone logs in). I know the Identity framework has support for lockouts, but this seems to be time restricted lockouts only. Is there a way that exists in the Identity framework to have permanent (unless manually changed) lockouts? I am trying to use as little custom code and design as possible, so that's why I am interested in knowing this as well. Particularly, I am interested in using the way the framework keeps track of unsuccessful login attempt counts, because I want to try to avoid implementing that manually as well.
Thank you.
Change userLockout type to bool in the Edit method and post should work.
To lock the user for a very long duration (a sub for permanent lock) after n failed attempts, one option is to set the DefaultLockoutTimeSpan to some x years in the future.
To check if a user is locked out, try UserManager.IsLockedOutAsync(userId)

ASP.NET web forms change bootstrap theme dynamicly

my main problem is that I want to change the bootstrap theme of my Website with a Dropdown list of themes and a button.
#model IEnumerable<ProjectManagementTool.ServiceReference1.Theme>
#{
ViewBag.Title = "Settings";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<form action="/Home/Settings" method="get">
<h2>Settings</h2>
<br />
<h4>Change Theme</h4>
#Html.DropDownList("GetThemes", null, htmlAttributes: new { #class = "form-control" })
<br />
<p>
<input class="btn btn-default" type="submit" value="Save"/>
</p>
#foreach (var item in Model)
{
#Html.DisplayFor(modelItem => item.Path)
<br/>
}
</form>
The foreach loop is just to show the diffrent paths.
I have a database aswell with a themes table:
Now the question is, how can I change the bootstrap?
I know that there is this code tho change the theme.
#Styles.Render("~/Content/flatly.bootstrap.css")
But it should be in the _layout.cshtml file to set it for the whole website.
The background is working with a service but this is not important here.
Screenshot of the website:
You could write an action that returns the path of the css file to be used depending on the user settings.
Something along the lines of
#Styles.Render(Html.Action("UserCssPath", "Settings").ToString());
Because you don't have access to a ViewModel in your layout page, the controller must use another mechanism to find out for which user the action was invoked. For example, we store a User Context object with the required data in the HttpContext.Current.Session. This could be written once on logon. The UserCssPath action can then access the DB to find the correct CSS path and return it: return Content(cssPath);

How does #Html.BeginForm() work? and search result in Microsoft ASP.Net MVC 5 tutorial?

I am working on MVC 5 Asp.Net and following this tutorial. I am wondering how the heck does this fetch the result when I click the Filter button?
There comes a point where this code is added in Movie/view/Index.cshtml
#using (Html.BeginForm())
{
<p> Title: #Html.TextBox("SearchString") <br />
<input type="submit" value="Filter" /></p>
}
Now as far as I know, it creates a textbox and a button on screen. But how is this button calling the search(index) function and passing the value of textbox in the function, I could not get this.
It's not a stupid question. #html.BeginForm() works like this. It has some parameters you could add to it like Action Controller FormType htmlAttributes. The way it works is that if you leave it empty it will look for a post action with the same name that on the page you are now, for example if you are in on the login page, it will look for a login post action. I always write what action and controller I want it to access.
#Html.BeginForm("AddUser", "Admin", FormMethod.Post, new { #class = "my_form"}) {
}
So your post action should accept parameters that your form contains, and that can be a Model ie a Product, ViewModel or single string parameters. In your case with the search your action should look like
[HttpPost]
public ActionResult Search(string SearchString)
{
//do something here
}
Please note here, for the search string to be passed into the method. The name of the <input> has to be the same as the parameter your action takes. So our form should be like this
#using (Html.BeginForm("Search", "YOUR CONTROLLER", FormMethod.Post)){
<p> Title: #Html.TextBox("SearchString") <br />
<input type="submit" value="Filter" /></p>
}
Hope this brings clarity.

Ajax Request that Post back a new page instead of loading partial

I have this HTML code in my view
#using (Ajax.BeginForm("AddJoke", "Home", new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "MyfriendsJokes" , InsertionMode= InsertionMode.InsertAfter}))
{
<div style="display:block">
<textarea placeholder="Post New Joke" id="newJoke" name="joke" rows="3" cols="50" style="float:left;position"></textarea>
<button type="submit" id="postnewjoke" style="float:left"> Post </button>
#Html.TextBoxFor(model => model.fileContent, new { type = "file", id = "fileuploaded", name = "fileuploaded" })
<div style="display:inline-block">
<input type="checkbox" name="geo" id="geo" style="width: 100%; float: left; display: block">
<input name="longitude" style="display:none"/>
<input name="latitude" style="display:none" />
<input name="user" style="display:none" value="#Model.user.Id"/>
<span>Include Location</span>
</div>
<span id="jokeError" style="color:red;font-size:14px;"></span>
</div>
}
<article id="MyfriendsJokes">
#Html.Partial("_NewJoke")
</article>
and this code in my controller
[HttpPost]
public PartialViewResult AddJoke(string joke, string user, HomePage page,HttpPostedFileBase fileuploaded, string longitude, string latitude)
{
Joke newJ = new Joke();
newJ.Key = Guid.NewGuid();
newJ.body = joke;
newJ.facebookID = user;
newJ.rank = 0;
newJ.time = DateTime.Now;
newJ.longitude = longitude;
newJ.latitude = latitude;
db.Jokes.Add(newJ);
HomePage page1 = new HomePage();
page1.user = Session["user"] as MyAppUser;
//db.SaveChanges();
return PartialView("_NewJoke", page1);
}
but instead of adding elements to the targeted div, it reload the page with a new whole page with just the elements of the partial view which is this
#using Jokes.Models
#using Microsoft.AspNet.Mvc.Facebook.Models
#model HomePage
<div style="display:block">
#Model.user.Name
</div>
can someone help and say what's wrong here to append elements to div instead of loading a new whole page?
Make sure that the jquery.unobtrusive-ajax.js script is referenced in your page. This is what AJAXifies all the output generated by the Ajax.* helpers. Without this script you only get a standard <form> element generated by the Ajax.BeginForm with a bunch of data-* attributes. The jquery.unobtrusive-ajax.js script analyzes those data-* attributes and subscribes to the submit event of the form, canceling the default action of making a full postback and sending an AJAX request to the server based on the data-* attributes.
It's important to mention that this script must be included AFTER jquery.js because it depends on it.
Also you seem to have some file input in your form and your controller action is taking an HttpPostedFileBase parameter. You should realize that you cannot upload files using an AJAX request and once you include this script your file uploads will simply stop working. In order to be able to upload files using AJAX you could either use some plugin such as jquery.form and Blueimp file upload or you could directly use the new XMLHttpRequest object that's built into modern browsers. The advantage of the plugins is that they do feature detection and will fallback to other techniques depending on the capabilities of the client browsers.

Resources