I built a module into my MVC site that allows administrators to create, modify and delete website users. However, I can not seem to update user information via the following code:
[HttpPost]
public ActionResult Edit(User user)
{
if (ModelState.IsValid)
{
try
{
//CHANGE EXISTING USER
MembershipUser siteUser = Membership.GetUser(user.Username);
siteUser.Email = user.Email;
siteUser.IsApproved = true;
siteUser.Comment = "User Update on " + DateTime.UtcNow;
siteUser.UnlockUser();
Membership.UpdateUser(siteUser);
if (!String.IsNullOrEmpty(user.Password))
{
siteUser.ChangePassword(siteUser.GetPassword(), user.Password);
}
unitOfWork.UsersRepository.Update(user);
unitOfWork.Save();
return RedirectToAction("Index");
}
catch (Exception err)
{
// CODE REMOVED FOR BREVITY
}
}
return View(user);
}
Any suggestions?
After further testing, it would appear that one line was causing the user information to reset.
siteUser.UnlockUser();
This line can not be called prior to saving user alterations via Membership.UpdateUser() or the changes will be lost. Hopefully this saves somebody some head scratching.
Related
I have code like this, where the commit is done at the end of the line. the goal is that when an error occurs while sending an email, then the commit will not be performed but will rollback. is something like this a good thing? or is there a better way than the one I'm currently implementing?
public async Task<IActionResult> Register(RegisterAccount register)
{
MyAccount myAccount = new();
using var transaction = _dbContext.Database.BeginTransaction();
bool afterRegister = false;
try
{
//code for check account
//code for set value register account
afterRegister = true;
_dbContext.Database.OpenConnection();
transaction.CreateSavepoint("register");
_dbContext.MyAccounts.Add(myAccount);
await _dbContext.SaveChangesAsync();
_dbContext.Database.CloseConnection();
//code for send email
transaction.Commit();
return RedirectToAction("RegisterConfirm", "Account", new { emailConfirm = myAccount.Email });
}
catch(Exception e)
{
if (afterRegister)
{
transaction.RollbackToSavepoint("register");
}
return View();
}
}
thank you for answering my question. good luck always
I'm trying to build a two-step custom registration for users in my ASP.NET MVC 4 application. This involves redirecting users after registration to the Edit view in the User Controller. I've tweaked the code in the stock Account Controller template to achieve this.
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Edit", "User", new { id = WebSecurity.CurrentUserId });
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
return View(model);
}
However when I run this I get a 404 on the address bar it shows
http://localhost:17005/User/Edit/-1
Which means it's going into the Edit action of user controller, but then ending up with a Id of -1. Any help would be appreciated.
Membership.GetUser().ProviderUserKey
Alternatively you could not pass a user id to the edit action and have the code use the currently logged in user.
RedirectToAction("Edit", "User");
In your user controller
[Authorize]
public ActionResult Edit()
{
object userId = Membership.GetUser().ProviderUserKey;
}
Please excuse brevity as I am currently on my mobile
I m creating library for my test projects.
Namespace is not working when i create library project.
Please help
#region(GET LOGGED USER ID)
static public Guid GetUserId()
{
Guid guUser = Guid.Empty;
try
{
MembershipUser muUser = Membership.GetUser();
if (muUser != null)
{
guUser = (Guid)muUser.ProviderUserKey;
Membership.GetUser(guUser, true);
}
else
{
//Log out and redirect to login page
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
}
catch (Exception ex)
{
ApplicationError.LogErrors(ex);
}
return guUser;
}
#endregion
This won't work if tested in a unit test solution, because your unit test solution must have the same .config file as the final application project. Otherwise it works perfectly.
I am trying to get to grips with the new Membership system introduced in ASP.NET MVC 5 and I've come across a small issue which I am pretty sure you will be able to help me with.
I am going based off this tutorial and have introduced custom properties to ApplicationUser such as Name, Surname, DOB, etc.
However, instead of creating the user, I am trying to update the currently logged in one. I am looking at the controller method which is currently used to change password.
public async Task<ActionResult> Manage(ManageUserViewModel model)
{
string userId = User.Identity.GetUserId();
bool hasLocalLogin = await IdentityManager.Logins.HasLocalLoginAsync(userId);
ViewBag.HasLocalPassword = hasLocalLogin;
ViewBag.ReturnUrl = Url.Action("Manage");
if (hasLocalLogin)
{
if (ModelState.IsValid)
{
IdentityResult result = await IdentityManager.Passwords.ChangePasswordAsync(User.Identity.GetUserName(), model.OldPassword, model.NewPassword);
if (result.Success)
{
return RedirectToAction("Manage", new { Message = "Your password has been changed." });
}
else
{
AddErrors(result);
}
}
}
else
{
// User does not have a local password so remove any validation errors caused by a missing OldPassword field
ModelState state = ModelState["OldPassword"];
if (state != null)
{
state.Errors.Clear();
}
if (ModelState.IsValid)
{
// Create the local login info and link it to the user
IdentityResult result = await IdentityManager.Logins.AddLocalLoginAsync(userId, User.Identity.GetUserName(), model.NewPassword);
if (result.Success)
{
return RedirectToAction("Manage", new { Message = "Your password has been set." });
}
else
{
AddErrors(result);
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
How exactly would I go on about updating an ApplicationUser's Surname for example? Do I need to call the DbContext or?
I hope my question is clear.
Explore IdentityManager.Store.UserManagement and IdentityManager.Store.Users.
ApplicationUser cUser = (ApplicationUser) await IdentityManager.Store.Users.FindByNameAsync(HttpContext.User.Identity.Name, new System.Threading.CancellationToken());
cUser.Surname = "New Something";
IdentityResult result1 = await IdentityManager.Store.SaveChangesAsync();
Above code is an example only. Basically you need to explore the Store property of IdentityManage.
When we used the Users object of our database context we ran into other tracking errors. In our application, we would retrieve users as such
var user = UserManager.FindById(userId);
Edit the properties:
user.StorageName = "gooblygook";
//whatever other properties you would like to use
And then we would save it with the UserManager in the controller:
UserManager.Update(user);
This is currently a working solution for us.
Mark the Person object as virtual in your ApplicationUser definition. That worked for me.
public class ApplicationUser : IdentityUser
{
public virtual Person Person { get; set; }
I have a probably very silly question but I'll has it anyway.
Here is the code in my controller for logging in
[HttpPost]
public ActionResult Index(LogonModel model, string ReturnUrl)
{
ReturnUrl = Request.QueryString["ReturnUrl"];
if (ModelState.IsValid)
{
if (UserRepository.validLogin(model.Username, model.Password))
{
UserLogRepository.createLogEntry("Log On", " has logged on to the Staff Portal.", "Entry/Exit");
if (ReturnUrl.Length > 1)
{
return Redirect(Request.QueryString["ReturnUrl"]);
}
else
{
return RedirectToAction("Dashboard", "Home");
}
}
else
{
ModelState.AddModelError("", Session["Error"].ToString());
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
As you can see i'm just checking if the returnurl has a length for testing purposes before I lock it down more. My issue is I get an "Object reference not set to an instance of an object." pointing to this line "if (ReturnUrl.Length > 1)"
Now the URL I have when a user has timed out from the site is this:
http://localhost/Dispatch2012/Staff/Home?ReturnUrl=Dispatch2012%2FStaff%2FCredential
As you can see, this is the standard redirect created by MVC 3 and i've tried to read the ReturnUrl as a standard query string but every time it says that object doesn't exist. What am I missing?
The way your controller is set up is strange to me, but let's dive into it:
[HttpPost]
public ActionResult Index(LogonModel model, string returnUrl) //changed
{
ReturnUrl = returnUrl; //changed
if (ModelState.IsValid)
{
if (UserRepository.validLogin(model.Username, model.Password))
{
UserLogRepository.createLogEntry("Log On", string.Format("{0} has logged on to the Staff Portal.", model.Username, "Entry/Exit"); //changed
if (ReturnUrl.Length > 1) //this should use IsLocalUrl
{
return Redirect(Request.QueryString["ReturnUrl"]);
}
else
{
return RedirectToAction("Dashboard", "Home");
}
}
else
{
ModelState.AddModelError("", Session["Error"].ToString());
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
A few things:
Your returnUrl needs to be checked to make sure it's a Local URL. There are a number of ways of doing this, and since you're using ASP.NET MVC 3, it's built in.
Why are you pulling the ReturnUrl out of the querystring when (if you've set up your view correctly), it's already passed in?
Do you have the following in your view?
<%= Html.Hidden("returnUrl", Url.Encode(Url.Action("ActionToRedirectTo", "ControllerName", new { id = Model.Whatever}))) %>
If so, when it posts, it will automatically get sent to the Index Action as the returnUrl parameter.
I'm betting since it's not working, you aren't actually sending the ReturnUrl back correctly, check what I said about the view. Oh, and make sure you're URL encoding the ReturnUrl.
Also, since it's a HttpPost, the querystring wouldn't have the return Url in it.