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

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)

Related

ASP.NET CORE MVC CRUD operation. Create form doesn't work anymore

Im am currently working on my CRUD operations for the website. On the attached pictures you can see that im using the create form for 2 purposes. 1. to create a user and 2 to edit a user. My Query is made well but when im adding a hidden id to get a certain user for editing. I cannot press create user anymore because i passed the piece of code in at picture 1. (the line i selected)
But when i remove this piece of code i can create a user but when im trying to edit a user when removing this piece of code it just edits the user and then creates a new one with the edited content. Does anyone know how to fix this issue?
p.s. i made screenshots of the controller and the piece of code where it goes wrong.
Kind regards,
Sem
[Piece of code where it goes wrong][1] [User controller][2] [User
controller][3] [Data transfer objects][4] [CreateOrUpdate
query/function][5]
[1]: https://i.stack.imgur.com/5XxDe.jpg [2]:
https://i.stack.imgur.com/s5pqh.jpg [3]:
https://i.stack.imgur.com/2vLXM.jpg [4]:
https://i.stack.imgur.com/R2OBn.jpg [5]:
https://i.stack.imgur.com/ADyv5.jpg
As per your screenshot you are calling same action method from UI, in that case you need to validate id in your action method to see if it valid then its update otherwise it will be add. Scenario first time your page loaded id will be empty and second time if you load then it will have id and same you can use for edit.
Hope this solve your problem 👍
First, you can copy your code using '{}' in above toolbar, so it will be more convenient for others to test your code.
but when im trying to edit a user when removing this piece of code it just edits the user and then creates a new one with the edited content.
You can check the database for the passed model, if id exists, create one, but if not, remove the old one and create this new one.
Here is a demo with linq, you can check the logic and modify my code to yours:
public async Task<IActionResult> ProcessCreate(UsersDto user)
{
if (!_context.UsersDtos.Any(x => x.Id == user.Id)) //if not exist, create one directly
{
_context.Add(user);
await _context.SaveChangesAsync();
}
else { //else, delete old one and create new one
var therow = _context.UsersDtos.Where(x => x.Id == user.Id).FirstOrDefault();
_context.Remove(therow);
_context.Add(user);
await _context.SaveChangesAsync();
}
return RedirectToAction("Index");
}
In Create.cshtml,you can show the id:
<form method="post" asp-action="ProcessCreate">
<div class="form-group" hidden>
<label asp-for="Id" class="control-label"></label>
<input asp-for="Id" class="form-control" />
<span asp-validation-for="Id" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="First_name" class="control-label"></label>
<input asp-for="First_name" class="form-control" />
<span asp-validation-for="First_name" class="text-danger"></span>
</div>
//.......
<div class="form-group">
<input type="submit" value="CreateOrEdit" class="btn btn-primary" />
</div>
Result:

TempData Dictionary is null after Redirect to page

So I have this issue that I am not unable to solve the way I think it's supposed to be solved.
I have an ASP.NET Core 2.1 Razor pages project. The code is pasted below and my problem is the following:
On the index page, I have a search form. The city name I enter in the search form gets used in the SearchResults OnPost method.
The OnPost redirects to OnGet which retrieves some results from the database based on the city passed in from the search form. From my understanding, TempData should be able to retain the value for the city passed in from the form, however, whenever I try to read TempData["CityFromForm"] in the OnGet method, the TempData dictionary is empty, even though that in the OnPost method I used the TempData.Keep method.
My current solution for this is using in memory cache to store the city value and pass it to the method that fetches the data from the database, but I would like to know why the TempData approach is not working.
On the index page on that project, there is a search from in which I enter a city for which I want to search the data, like so:
#model SearchDataViewModel
<form asp-page="/Search/SearchResults" method="post" class="col s6">
<div class="row">
<div class="input-field col s12">
<input placeholder="Please enter a city" type="text" name="City" class="validate autocomplete" id="autocomplete-input" autocomplete="off" />
<label for="City">City</label>
</div>
</div>
<div class="row">
<div class="input-field col s6">
<input id="StartDate" name="StartDate" type="text" class="datepicker datepicker-calendar-container">
<label for="StartDate">Start date</label>
</div>
<div class="input-field col s6">
<input id="EndDate" name="EndDate" class="datepicker datepicker-calendar-container" />
<label for="EndDate">End date</label>
</div>
</div>
<input type="submit" hidden />
</form>
What matters in that form is the city. That form gets sent to the SearchResults razor page.
SearchResults.cshtml.cs
public IActionResult OnPost()
{
// Cache search form values to persist between post-redirect-get.
var cacheEntry = Request.Form["City"];
_cache.Set<string>("City", cacheEntry);
TempData["CityFromFrom"] = Request.Form["City"].ToString();
TempData.Keep("CityFromForm");
return RedirectToPage();
}
// TODO: Find a better way to persist data between onPost and OnGet
public async Task OnGet(string city)
{
City = _cache.Get<string>("City");
var temp = TempData["CityFromForm"];
// Here I'd like to pass the TempData["CityFromForm"] but it's null.
await GetSearchResults(City); // this method just gets data from the database
}
TempData keys are prefixed by "TempDataProperty-". So if you have a key named "City", you access it via TempData["TempDataProperty-City"].
See https://www.learnrazorpages.com/razor-pages/tempdata
You also have a typo in the line where you assign the tempdata value: TempData["CityFromFrom"] should be TempData["CityFromForm"], I suspect.
So here is what I came up with, basically I get a city string from the search form. In the OnPost method I redirect to page where I add a route value which OnGet method can use.
In SearchResults.cshtml I added a #page "{city?}"
The url ends up looking like: https://localhost:44302/Search/SearchResults?searchCity={city}
In SearchResults.cshtml.cs
public async Task OnGet()
{
City = HttpContext.Request.Query["searchCity"];
PetGuardians = await GetSearchResults(City);
}
public IActionResult OnPost(string city)
{
return RedirectToPage(new { searchCity = city });
}

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.

create login in asp.net(Proprietary of admin login)

the name of God
and Hi to you
I created some login pages in asp.net
i want use SHA-1 algoritm for my passwords, it's mean, controller get password then send to SHA-1 function and then save it to db.
first: I have these cods in cshtml
<input type="text" name="Username" required="required" />
<label for="Username">Username</label>
<div>
<input type="password" name="Password" required="required"/>
<label for="Password">Password</label>
</div>
<div type="submit" class="button-container">
<button><span>Go</span></button>
</div>
after submit username and pass will be checked and if those be true an new page will be open and user login happen successfully but I don't have any idea about to show my login happened, let explain it whit a sample
I submit my login page and then this page will be open :http://localhost:19926/Home/Home because I used these codes(%1)
in html:
#using (Html.BeginForm("webAdminAccess", "Authentication")){
...myhtml codes...}
in controller:
public ActionResult webAdminAccess(string Username, string Password)
{
if (mLO.webAdminAccess(Username, Password))
{
return RedirectToAction("Home", "Home");
}
else
{
return View("webAdminAccessWrong");
}
}
and there is no difference if I run my project in visual and I put this link http://localhost:19926/Home/Home in my brower(%2)
now let me ask my question:
how make a difference between these two?(%1,%2)
what is Characteristic or Proprietary of a page that was open whit a login?
how make a difference between login as admin or login as client?
(I have admin class, authenticationController and use my sql)
tnx for you help

Passing so far entered data to controller

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.

Resources