Trying to update my users password using AddPasswordAsync but passwords always left as null - asp.net

So i've got 2 tables one for Editing user roles and one fore editing user accounts the roles table allows me to delete and add roles perfectly fine but i'm having trouble with updating passwords. The passwords seem to be getting deleted but not updated with the new one specified.
<br>
<h3>Roles Table</h3>
<table class="table table-striped">
<thead>
<tr> <th>Id</th><th>User Roles</th> </tr>
</thead>
<tbody>
#foreach (var roles in Model.roles)
{
<tr>
<td> #roles.Id</td>
<td> #roles.Name</td>
<td><a class="btn btn-sm btn-danger order-button float-right" asp-page="/ManageRoles" asp-route-id="Delete" asp-page-handler="Delete">Delete Roles </a></td>
</tr>
}
</tbody>
</table>
<form method="post">
<div class="flex">
<div>
<button type="submit" class="btn btn-primary">Submit</button>
</div>
<div class="input-group">
<input type="text" class="form-control" asp-for="#Model.roleName">
</div>
</div>
</form>
<table class="table table-striped" style="margin-top: 100px;">
<thead>
<tr> <th>Id</th><th>User Account</th> </tr>
</thead>
<tbody>
#foreach (var users in Model.users)
{
<tr>
<td> #users.Id</td>
<td> #users.Email</td>
<td><a class="btn btn-sm btn-danger order-button float-right" asp-page="/ManageRoles" asp-route-id="DeleteUser" asp-page-handler="DeleteUser">Delete User </a></td>
<td>
<form method="post" asp-page-handler="Update" asp-route-id="#users.Id">
<div class="input-group">
<input type="text" class="form-control" asp-for="#Model.NewPassword">
<button type="submit" class="btn btn-default">Update Password </button>
</div>
</form>
</td>
</tr>
}
</tbody>
</table>
So heres my Page model class below
{
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<AppUser> _userManager;
public List <IdentityRole> roles { get; set; }
[BindProperty(SupportsGet = true)]
public string Id { get; set; }
[BindProperty]
public string roleName { get; set; }
public List <AppUser> users { get; set; }
[BindProperty]
public string userId { get; set; }
[BindProperty]
public string NewPassword { get; set; }
public AdminDashboardModel(RoleManager<IdentityRole> roleManager, UserManager<AppUser> userManager)
{
_roleManager = roleManager;
_userManager = userManager;
}
public void OnGet()
{
roles = _roleManager.Roles.ToList();
users = _userManager.Users.ToList();
}
public async Task <IActionResult> OnGetDeleteAsync()
{
var role = await _roleManager.FindByIdAsync(Id);
await _roleManager.DeleteAsync(role);
return RedirectToPage("/AdminDashboard");
}
public async Task<IActionResult> OnPostAsync()
{
if (roleName != null)
await _roleManager.CreateAsync(new IdentityRole(roleName.Trim()));
return RedirectToPage("/AdminDashboard");
}
public async Task<IActionResult> OnGetDeleteUserAsync()
{
var user = await _userManager.FindByIdAsync(Id);
await _userManager.DeleteAsync(user);
return RedirectToPage("/AdminDashboard");
}
public async Task<IActionResult> OnPostUpdateAsync()
{
var user = await _userManager.FindByIdAsync(Id);
await _userManager.RemovePasswordAsync(user);
await _userManager.AddPasswordAsync(user, NewPassword);
return RedirectToPage("/AdminDashboard");
}
}
}

Try it with this bit of code:
public async Task<IActionResult> OnPostUpdateAsync()
{
var user = await _userManager.FindByIdAsync(Id);
var token = await _userManager.GeneratePasswordResetTokenAsync(user)
var result = await _userManager.ResetPasswordAsync(user, token, NewPassword);
//validate result
if(result.Succeeded) {
return RedirectToPage("/AdminDashboard");
}
//handle errors
throw new Exception()
}
You don't actually need to validate the result, but things tend to go sideways. Always better to have error handling.
The generated token validates the action of changing the password against the system. If a user would want to reset their password, because they forgot theirs, you would also need to generate such a token, send it to their email and then let the user choose a new password.
Alternatively you could just use UserManager.UpdatePasswordHash:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.usermanager-1.updatepasswordhash?view=aspnetcore-5.0
var result = await _userManager.UpdatePasswordHash(user, NewPassword, true|false);

Related

How to Change a Single Select Dropdown to a Multi-Select Dropdown?

Still in learning stages and want to learn how to create a multi-select dropdown.
I currently have a single select dropdown working. Here is the code for that:
Snip from Municipality Model:
[Display(Name = "Associated Agencies")]
public string? AssocAgencies { get; set; }
Snip from Municipality Create.cs:
public class CreateModel : PageModel
{
private readonly ApplicationDbContext _db;
public Municipality Municipality { get; set; }
public CreateModel(ApplicationDbContext db)
{
_db = db;
}
public IEnumerable<Agency> DisplayAgencyData { get; set; }
public async Task OnGet()
{
await _db.Agency.Select(a => a.AgencyName).ToListAsync();
DisplayAgencyData = await _db.Agency.ToListAsync();
}
public async Task<IActionResult> OnPost()
{
if (ModelState.IsValid)
{
_db.Municipality.Add(Municipality);
await _db.Municipality.AddAsync(Municipality);
await _db.SaveChangesAsync();
TempData["success"] = "Municipality added successfully.";
return RedirectToPage("Index");
}
return Page();
}
Snip from Municipality Create.cshtml:
<table class="table table-bordeless" style="width:100%">
<tr>
<td style="width: 25%">
<div class="mb-3">
<label asp-for="Municipality.AssocAgencies"></label>
<select asp-for="Municipality.AssocAgencies" id="Select2" class="form-select" asp-items="#(new SelectList(Model.DisplayAgencyData.OrderBy(x => x.AgencyName),"AgencyName", "AgencyName"))"><option value="" selected disabled>---Select Agency---</option></select>
</div>
</td>
</tr>
</table>
Values from the Agency model are:
| Id | AgencyName |
|:--:|:--------------------|
|1 |N/A |
|2 |Board of Appeals |
|3 |City Council |
|4 |Planning Board |
|5 |Planning Commission |
|6 |Town Board |
Since I have not done a multi-select before - Is the stored result in the data table cell a comma delimited value?
Ie, if the user selects options 2, 4 and 6 is the value "Board of Appeals, Planning Board, Town Board" OR "2, 4, 6" if someone were to bind the Ids rather than the values?
Also, is there an advantage to doing it this way OR is it more advantageous to have multiple separate single select dropdowns? I'm thinking it could be either way depending on how you wanted to pull the data.
Thanks in advance for your help!
1.If you want to make dropdown multiple you need change string? to List<string>?.
2.Then if you want to pass the Ids instead of name, you need change the second parameter of new SelectList:
new SelectList(Model.DisplayAgencyData.OrderBy(x => x.AgencyName),"Id", "AgencyName")
Whole working demo below
Model:
public class Municipality
{
[Display(Name = "Associated Agencies")]
public List<string>? AssocAgencies { get; set; }
}
public class Agency
{
public int Id { get; set; } //be sure your model contains id property
public string AgencyName { get; set; }
}
Page:
#page
#model CreateModel
<form method="post">
<table class="table table-bordeless" style="width:100%">
<tr>
<td style="width: 25%">
<div class="mb-3">
<label asp-for="Municipality.AssocAgencies"></label>
<select asp-for="Municipality.AssocAgencies" id="Select2" class="form-select" asp-items="#(new SelectList(Model.DisplayAgencyData.OrderBy(x => x.AgencyName),"Id", "AgencyName"))"><option value="" selected disabled>---Select Agency---</option></select>
</div>
</td>
</tr>
</table>
<input type="submit" value="Post"/>
</form>
PageModel:
[BindProperty] //be sure add this....
public Municipality Municipality { get; set; }
public IEnumerable<Agency> DisplayAgencyData { get; set; }
public void OnGet()
{
DisplayAgencyData = await _db.Agency.ToListAsync();
}
public async Task<IActionResult> OnPost()
{
//do your stuff....
}
Note:
If you do not want to change string? to List<string>?, you can add an extra property which receives the multiple selected value and set the value forAssocAgencies by adding Quotes around string in a comma delimited list of strings.
Page:
<form method="post">
<table class="table table-bordeless" style="width:100%">
<tr>
<td style="width: 25%">
<div class="mb-3">
<label asp-for="SelectedAgency"></label>
//don't forget change here asp-for .......
<select asp-for="SelectedAgency" id="Select2" class="form-select" asp-items="#(new SelectList(Model.DisplayAgencyData.OrderBy(x => x.AgencyName),"Id", "AgencyName"))"><option value="" selected disabled>---Select Agency---</option></select>
</div>
</td>
</tr>
</table>
<input type="submit" value="Post"/>
</form>
PageModel:
[BindProperty]
public Municipality Municipality { get; set; }
public IEnumerable<Agency> DisplayAgencyData { get; set; }
[BindProperty]
public List<string> SelectedAgency { get; set; } //add this .
public void OnGet()
{
DisplayAgencyData = await _db.Agency.ToListAsync();
}
public void OnPost()
{
Municipality.AssocAgencies = string.Join(",", SelectedAgency.Select(x => string.Format("'{0}'", x.Replace("'", "''"))));
}

How to Edit Image in Existing Member

I Use bootstrap cards and i want picture can also edit on existing user or i Edit picture of any member
This is my Controller
This is my edit Functionality where i can edit My Member so i want i update all existing user picture
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var member = await _context.Member.FindAsync(id);
if (member == null)
{
return NotFound();
}
return View(member);
}
// POST: HomePage/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("MemberId,Name,Gender,DOB,MaritalStatus,Address,PhoneNo,Skills,Hobbies,JobTitle,Technology")] Member member)
{
if (id != member.MemberId)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(member);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MemberExists(member.MemberId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(member);
}
This is a View of my project this is same as create class
<div class="row">
<div class="form-group col-md-4">
<label class="col-md-2 control-label">MemberImage</label>
<div class="col-md-10">
<div id="chooseFile">
<input class="form-control" type="file" name="photo" accept="image/*" />
</div>
</div>
</div>
</div>
when i edit the one existing member
2.i add image
and save it will save the img but now they cannot save image
Exception Occurs when i open edit
*Edit Page Code
<div class="row">
<div class="form-group col-md-4">
<label class="col-md-2 control-label">MemberPicture</label>
<div class="col-md-10">
<img src="~/ImageName/Cover/#Model.Member.ImageName"
class="rounded-square"
height="50" width="75"
style="border:1px"
asp-append-version="true" accept="image/*" />
<span>#Model.Member.ImageName</span>
<div id="chooseFile">
<input class="form-control" type="file" name="photo" accept="image/*" />
</div>
</div>
</div>
</div>
View Model
using System;
using TeamManagement.Models;
namespace TeamManagement.ViewModel
{
public class MemberViewModel
{
public Member Member { get; set; }
public IFormFile? Photo { get; set; }
}
}
This is my edit Functionality where I can edit My Member so I want I update all existing user picture?
If you look into the Member List, it usually contains rows of users along with Id. So we have to find the particular Id and then need to retrieve the value of that Id finally we will update the existing value with the new value. As seen on the screenshot below
Algorithm
From The Member List Click On Particular Member Id
Find The Member Information By that Id
Load The Edit Page With That Id Same As Create Member Page
After Required Change Submit the Edit Page Which Containing the Member Model Data With A ID
Save the Image Into Folder First Same As Create
Search The Member Object By Id
Set New Value Into The Member Object You Have Found In Step 6
Save The Context And Redirect To Member List
Controller Action For Loading Edit Page
public async Task<IActionResult> EditMember(int memberId)
{
var memeber = await _context.Members.FindAsync(memberId); // Getting member by Id from database
return View(new MemberViewModel() { Member = memeber });
}
View Model
public class MemberViewModel
{
public Member Member { get; set; }
public IFormFile? Photo { get; set; }
}
View Edit
#model DotNet6MVCWebApp.Models.MemberViewModel
<div>
<form asp-action="EditMember" method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly"></div><input type="hidden" asp-for="Member.MemberId" />
<div>
<h4><strong>Member Details</strong> </h4>
<table class="table table-sm table-bordered table-striped">
<tr>
<th> <label asp-for="Member.Name"></label></th>
<td> <input asp-for="Member.Name" class="form-control" placeholder="Enter member name" /><span asp-validation-for="Member.Name"></span></td>
</tr>
<tr>
<th> <label asp-for="Member.Gender"></label></th>
<td>
<select asp-for="Member.Gender" class="form-control">
<option value="Male">Male</option>
<option value="Female">Female</option>
</select>
<span asp-validation-for="Member.Gender"></span>
</td>
</tr>
<tr>
<th> <label asp-for="Member.DOB"></label></th>
<td> <input asp-for="Member.DOB" class="form-control" placeholder="Enter animal category" /><span asp-validation-for="Member.DOB"></span></td>
</tr>
<tr>
<th> <label asp-for="Photo"></label></th>
<td>
<img src="~/ImageName/Cover/#Model.Member.ImageName"
class="rounded-square"
height="50" width="75"
style="border:1px"
asp-append-version="true" accept="image/*" />
<span>#Model.Member.ImageName</span>
<div id="chooseFile"><input type="file" name="photo" accept="image/*" /></div>
</td>
</tr>
<tr>
<th> <button type="submit" class="btn btn-primary" style="width:107px">Update</button></th>
<td> </td>
</tr>
<tr>
<th>#Html.ActionLink("Back To List", "MemberList", new { /* id=item.PrimaryKey */ }, new { #class = "btn btn-success" })</th>
<td> </td>
</tr>
</table>
</div>
</form>
</div>
Here make sure your src="~/ImageName/Cover/#Model.Member.ImageName" is correct as per your picture location. Otherwise picture will not be displayed
Controller When Submit Edit
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditMember(MemberViewModel model, IFormFile photo)
{
if (photo == null || photo.Length == 0)
{
return Content("File not selected");
}
//Save The Picture In folder
var path = Path.Combine(_environment.WebRootPath, "ImageName/Cover", photo.FileName);
using (FileStream stream = new FileStream(path, FileMode.Create))
{
await photo.CopyToAsync(stream);
stream.Close();
}
//Bind Picture info to model
model.Member.ImageName = photo.FileName;
//Finding the member by its Id which we would update
var objMember = _context.Members.Where(mId => mId.MemberId == model.Member.MemberId).FirstOrDefault();
if (objMember != null)
{
//Update the existing member with new value
objMember!.Name = model.Member.Name;
objMember!.Gender = model.Member.Gender;
objMember!.DOB = model.Member.DOB;
objMember!.ImageName = model.Member.ImageName;
objMember!.ImageLocation = path;
await _context.SaveChangesAsync();
}
return RedirectToAction("MemberList");
}
Here we will update the _context.SaveChangesAsync() instead of adding new data. This is the key point for edit. Update the information which we have got by ID
Output

Unable to pass view model list in CSHTML to controller

I have a View Model, View, and Controller that works great displaying the data, but I cannot get the data entered in the form to save to the controller. I've tried using a list, array, and a list for the view model.
Here's my view model:
public class AssignedHostData
{
public int HostID { get; set; }
public string HostName { get; set; }
public bool Assigned { get; set; }
[DisplayName("Additional Details")]
[DataType(DataType.MultilineText)]
public string AddDetails { get; set; }
}
Here's the section of my view that displays the data:
<table class="table">
<tr>
#{
int cnth = 0;
List<Support_Web.Models.ViewModels.AssignedHostData> hosts = ViewBag.Hosts;
foreach (var host in hosts)
{
if (cnth++ % 1 == 0)
{
#:</tr><tr>
}
#:<td>
<input type="checkbox"
name="selectedHosts[#cnth].HostID"
id="selectedHosts_[#cnth]_HostID"
value="#host.HostID"
#(Html.Raw(host.Assigned ? "checked=\"checked\"" : "")) />
#host.HostID #: #host.HostName
#:</td>
#:<td>
<input type="text" name="selectedHosts[#cnth].AddDetails" id="selectedHosts_[#cnth]_AddDetails" value="#host.AddDetails" />
#:</td>
}
#:</tr>
}
</table>
And here're the parameters from my Edit controller. The selectedProducts list returns an empty list every time:
public async Task<IActionResult> Edit(int? id, string[] selectedProducts, List<HostCheckListItem> selectedHosts)
My view needed to be formatted this way:
#{
int cnth = 0;
List<Support_Web.Models.ViewModels.AssignedHostData> hosts = ViewBag.Hosts;
for (int i = 0; i < hosts.Count; i++)
{
<tr>
<td>
<input type="checkbox"
name="selectedHosts[#i].HostID"
value="#hosts[#i].HostID"
#(Html.Raw(hosts[#i].Assigned ? "checked=\"checked\"" : "")) />
#hosts[#i].HostName
</td>
<td>
<input type="text" name="selectedHosts[#i].AddDetails" value="#hosts[#i].AddDetails" />
</td>
</tr>}
}
If you want to transfer the modified model data list in the view to
the controller, you can use "asp-for" to bind in the form.
Please refer to the following for details:
public class ProductController : Controller
{
private readonly MydbContext _context;
public ProductController (MydbContext context)
{
_context = context;
}
public IActionResult Index()
{
ViewBag.Hosts = _context.AssignedHostData.ToList();
return View();
}
public async Task<IActionResult> Edit(List<AssignedHostData> Hosts)
{
List<AssignedHostData> selectedHosts = Hosts.Where(x => x.Assigned == true).ToList();
return View();
}
}
Edit.cshtml:
<form asp-controller="Product" asp-action="Edit" method="post">
<table class="table">
<tr>
#{
int cnth = 0;
List<WebApplication_core.Models.AssignedHostData> Hosts = ViewBag.Hosts;
for (int i = 0; i < Hosts.Count; i++)
{
<tr>
<td>
<input id="Hidden1" type="hidden" asp-for="#Hosts[i].HostID" />
<input type="checkbox" asp-for="#Hosts[i].Assigned" />
#Hosts[i].HostName
<input id="Hidden2" type="hidden" asp-for="#Hosts[i].HostName" />
</td>
<td>
<input type="text" asp-for="#Hosts[i].AddDetails" />
</td>
</tr>
}
}
</table>
<input id="Button1" type="submit" value="Edit" />
</form>
Here is the debug result:

HTTP Status 400 – Bad Request is being displayed on adding #Valid annotation to my controller

I'm trying to add a Validation to my form using Hibernate Validator to my project. On submitting the page below is the error message being displayed. But when I remove #Valid annotation from my Controller (UserController.handleLogin) method I'm able to submit the details. Can someone please suggest what am I missing?
UserController.java
#Controller
public class UserController {
#RequestMapping(value = { "/", "/index" })
public String index(Model model) {
model.addAttribute("command", new LoginCommand());
return "index";
}
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String handleLogin(Model model) {
model.addAttribute("err", null);
return "redirect:index";
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String handleLogin(#Valid #ModelAttribute("command") LoginCommand cmd, Model model, HttpSession session, Errors error) {
if(error.hasErrors()) {
return "index";
}
try {
User loggedInUser = userService.doLogin(cmd.getLoginName(), cmd.getPassword());
if (loggedInUser == null) {
model.addAttribute("err", "Login failed, enter valid credentials");
return "index";
} else {
if (loggedInUser.getRole().equals(UserService.ROLE_ADMIN)) {
// add user to session
addUserToSession(loggedInUser, session);
return "redirect:admin/dashboard";
} else if (loggedInUser.getRole().equals(UserService.ROLE_USER)) {
// add user to session
addUserToSession(loggedInUser, session);
return "redirect:user/dashboard";
} else {
model.addAttribute("err", "Invalid User Role");
return "index";
}
}
} catch (UserBlockedException e) {
model.addAttribute("err", e.getMessage());
return "index";
}
}
}
LoginCommand.java
public class LoginCommand {
#Size(max = 20, min = 3, message = "Login Name must be between 3 and 20 characters")
private String loginName;
#Size(max = 20, min = 3, message = "Password cannot be empty")
private String password;
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
index.jsp
<s:url var="url_login" value="/login"></s:url>
<f:form action="${url_login}" modelAttribute="command" valign="middle" method="POST">
<table border="1" align="center" width="40%">
<thead>
<tr>
<td colspan="2" valign="middle" align="center">
<h2>Login here</h2></td>
</tr>
</thead>
<tbody>
<tr>
<td align="center">Username: </td>
<td align="center">
<f:input path="loginName" placeholder="Enter your username"/>
<f:errors path="loginName" />
</td>
</tr>
<tr>
<td align="center">Password: </td>
<td align="center">
<f:password path="password" placeholder="Enter your password"/>
<f:errors path="password"/>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<f:button>Login</f:button>
New User Registration
</td>
</tr>
</tbody>
</table>
</f:form>
Add Binding result to your method.
BindingResult bindingResult

Asp.net core razor pages [BindProperty] doesnt work on collections

Im trying to use [BindProperty] annotation in asp.net core razor pages in order to Bind an Ilist<T> collection of one of my model classes so i can edit some of them at once, but it doesnt work at all, every time in OnPostAsync function the collection is empty, and neither the changes that i made on data nor it default values wont post back to the server, but when its a singel object [BindProperty] works fine and the values post back and can be changed, i also tried wraping a collection (i.e list<T>) in an object but it didnt work either way, so is there any way for doing so or i should lets say send a edit request for every object in that collection and edit them one by one(which cant be done in razor pages easilly and need some ajax calls)??
For binding IList between RazorPage and PageModel, you will need to use Product[i].Name to bind property.
Here are complete steps.
Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
PageModel
public class IndexModel : PageModel
{
private readonly CoreRazor.Data.ApplicationDbContext _context;
public IndexModel(CoreRazor.Data.ApplicationDbContext context)
{
_context = context;
}
[BindProperty]
public IList<Data.Product> Product { get; set; }
public async Task OnGetAsync()
{
Product = await _context.Product.ToListAsync();
}
public async Task OnPostAsync()
{
var product = Product;
}
}
View
<form method="post">
<table class="table">
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Product[0].Name)
</th>
<th></th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Product.Count(); i++)
{
<tr>
<td>
<input hidden asp-for="Product[i].Id" class="form-control"/>
<input asp-for="Product[i].Name" class="form-control" />
</td>
<td>
<a asp-page="./Edit" asp-route-id="#Model.Product[i].Id">Edit</a> |
<a asp-page="./Details" asp-route-id="#Model.Product[i].Id">Details</a> |
<a asp-page="./Delete" asp-route-id="#Model.Product[i].Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>

Resources