I'm working on an ASP.NET web forms project that has an implementation of a user repository that is acting strangely. I'm experiencing the following behavior:
User logs in with a valid username/password. They get in successfully.
User logs out.
I change the password in the database (for argument's sake).
User logs in again using the new password. However, the login is rejected as invalid.
If I set a breakpoint at the "user" variable in the ValidateUser() method and inspect the password, it has the OLD password even though the database clearly has the new password saved. It seems to me like there's some problem with the architecture of the repository that is causing it to load an old context or something along those lines, but it creates a new PortalContext() each time the class is instantiated. Any ideas?
public class BaseRepository
{
protected readonly PortalContext Context = new PortalContext();
}
public class UserRepository : BaseRepository, IUserRepository
{
public bool ValidateUser(string username, string password)
{
var user = Context.Users.FirstOrDefault(x => x.LoginName == username);
if (user == null) return false;
return user.Password == password;
}
}
.NET CODE BEHIND
public partial class _default
{
private readonly IUserRepository _userRepository = new UserRepository();
protected void btnSubmit_Click(object sender, EventArgs e)
{
if (_userRepository.ValidateUser(txtUsername.Text, txtPassword.Text)
{
// Log user in
}
}
}
EDIT: If I update ValidateUser() to the following code, the problem goes away. This seems to confirm that it has something to do with it hanging on to the old context.
public bool ValidateUser(string username, string password)
{
using (var context = new PortalContext())
{
var user = context.Users.FirstOrDefault(x => x.LoginName == username);
if (user == null) return false;
return user.Password == password;
}
}
I think I had the same issue. The problem is that MembershipProvider is instantiated only once and you probably created context in its constructor and thats why it works when you moved your context initialization in method ValidateUser.
The entity framework only looks for changes if one of the entity key's has changed.
When I had a similar issue, I made the dateModified field (every table I make such a field to track when it was changed) as a entity key, since then all changes are coming in perfect and fast. in the same context instance.
Related
I am new with C# and MVC aplications.
I have weird issue with password change in MVC aplication.
The thing is next, when i fill my form and enter old and new password and hit enter, everything seams to went fine but the password is not updated in databse. Then when I do from second try it went fine.
[Authorize]
[HttpPost]
public ActionResult ChangePassword(ChangePasswordModel model)
{
if (ModelState.IsValid)
{
bool changePasswordSucceeded;
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
if (changePasswordSucceeded)
{
var user = GetUser();
user.PasswordEntropyScore = model.PasswordEntropyScore;
var userRepo = new UserRepository(MvcApplication.DbSession);
userRepo.Update(user);
return this.RedirectToAction("ChangePasswordSuccess");
}
else
{
ModelState.AddModelError(string.Empty, "The current password is incorrect or the new password is invalid.");
}
}
return this.View(model);
}
UserRepository Update() method is here:
public class UserRepository : Repository<User>
{
private Logger logger;
public UserRepository(ISession sesh) : base(sesh)
{
this.logger = LogManager.GetCurrentClassLogger();
}
public new bool Update(User user)
{
if (string.IsNullOrWhiteSpace(user.UserName) || string.IsNullOrWhiteSpace(user.Email))
{
var ex = new ArgumentException("Username or Email cannot be blank");
this.logger.Error("User.Update - username or email was null/empty string.", LoggerHelper.GetErrorString(ex, user));
throw ex;
}
return base.Update(user);
}
}
And repository base.Update() method:
public class Repository<T> : IRepository<T>
where T : Entity
{
protected readonly ISession session;
protected static Logger logger;
public Repository()
{
throw new NotImplementedException("Must instantiate repositories with an ISession");
}
public Repository(ISession sesh)
{
this.session = sesh;
logger = new LogFactory().GetCurrentClassLogger();
}
public bool Update(T entity)
{
this.session.SaveOrUpdate(entity);
this.session.Flush();
return true;
}
}
So all this code went fine when first time I tried to change password but it doens't udapted it in database. Then when I try the second time to change the password then it update in database.
If I change the code to directly call base.Update() method instead to call first UserRepostirory.Update() method as wrapper then it is fine.
Does anyone has idea what the issue is.
there is not much comment in your code to understand what you really did but if you use entity framework you can simply do like below:
entitymodel model= new entitymodel();
var query= model.users.first(o=>userID==inputID);
query.password=inputPassword;
model.SaveChanges();
As per the code I do not see any issues. Is it possible to add try catch block to Repository class Update Method and check if any exception is occurred when you are trying to save for the first time.
Can you check if MvcApplication.DBSession is set before hitting Update method for the first time
There is a possibility of having entity framework local cache which may be updating password to older one. Perform below steps.
Remove below lines from your code.
Change password
Restart application and check if password is changed or not.
If password is changed then clear entity framework local cache(by code).
NOTE: There is no need of updating password in db again. ChangePassword function makes changes to DB directly.
I'm using windows authentication and custom roles. I've extended the WindowsPrincipal because I want to include additional information about the user based on a User class I've added. When I run the application, it sees the CustomPrincipal assigned to the built-in User, but not the additional "user" property I've added. I'm sure I'm doing something really dumb, but this is my first run into the C# ASP world and could really appreciate some help. Here is my custom principal and global.asax
Custom principal:
public class CustomPrincipal : WindowsPrincipal
{
List<string> _roles;
private User thisUser = new User();
public CustomPrincipal(WindowsIdentity identity)
: base(identity)
{
_roles = new List<string>(Roles.GetRolesForUser());
user = thisUser.getDarUser(identity.Name);
}
public User user { get; private set; }
public override bool IsInRole(string role)
{
if (base.IsInRole(role) || _roles.Contains(role) || _roles.Contains("Admin") || _roles.Contains("Dev"))
{
return true;
}
else
{
return false;
}
}
}
And Global.asax:
protected void Application_AuthorizeRequest(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated)
{
IIdentity thisId = User.Identity;
WindowsIdentity wi = (WindowsIdentity)thisId;
CustomPrincipal cp = new CustomPrincipal(wi);
HttpContext.Current.User = cp;
Thread.CurrentPrincipal = cp;
}
}
Thanks again for any direction.
Looking into it a little more I found the fail was in how I was trying to access the principal in my views. Thanks to ASP.NET MVC - Set custom IIdentity or IPrincipal, while this was not related to my Windows Auth type project, it did lead me to the correct usage of my principal.
What I was doing WRONG:
#User.user.myproperty
Change to:
#((User as CustomPrinicpal).user.myproperty
Hopefully this helps another newb not make the same bonehead mistake
I'm trying to get up and running with an MVC 5 project using ASP.NET Identity 2.0. My starting point is the example app in this tutorial. My initial page is Home/Index. When I try to do a search (expecting a null return value) the app simply hangs and I can't figure out why. Instantiation of the db context is causing the Seed() method to be called, which seems normal, but then it hangs on the roleManager.FindByName(roleName) call (I've commented it in the code below). Pausing the debugger showed where it was stuck but I don't know where to go from there. Relevant classes are as follows.
Controller:
public class HomeController : ApplicationController
{
public ActionResult Index()
{
var user = db.Users.Find("dummyVal");
return View();
}
[Authorize]
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
Base Controller:
public abstract class ApplicationController : Controller
{
protected ApplicationDbContext db;
public ApplicationController()
{
db = new ApplicationDbContext();
}
}
DB Initializer:
public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context) {
InitializeIdentityForEF(context);
base.Seed(context);
}
//Create User=Admin#Admin.com with password=Admin#123456 in the Admin role
public static void InitializeIdentityForEF(ApplicationDbContext db) {
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
const string name = "admin#example.com";
const string password = "Admin#123456";
const string roleName = "Admin";
//Create Role Admin if it does not exist
// EXECUTION HANGS ON THIS CALL...
var role = roleManager.FindByName(roleName);
if (role == null) {
role = new IdentityRole(roleName);
var roleresult = roleManager.Create(role);
}
var user = userManager.FindByName(name);
if (user == null) {
user = new ApplicationUser { UserName = name, Email = name };
var result = userManager.Create(user, password);
result = userManager.SetLockoutEnabled(user.Id, false);
}
// Add user admin to Role Admin if not already added
var rolesForUser = userManager.GetRoles(user.Id);
if (!rolesForUser.Contains(role.Name)) {
var result = userManager.AddToRole(user.Id, role.Name);
}
// Create dummy user
const string dummyUserName = "PeterVenkman";
const string dummyUserPwd = "gf%^^ftf83X";
var dummyUser = userManager.FindByName(dummyUserName);
if (dummyUser == null) {
dummyUser = new ApplicationUser { UserName = dummyUserName, Email = dummyUserName };
var result = userManager.Create(dummyUser, dummyUserPwd);
result = userManager.SetLockoutEnabled(dummyUser.Id, false);
}
}
}
The error is because you are instantiating the dbContext object in the controller and not getting it from the owin context. The presence of two different context objects one in the controller and one in the startup working on the same db is causing this deadlock. Simplest way to resolve this is replace the code
db = new ApplicationDbContext();
with
db = System.Web.HttpContext.Current.GetOwinContext().Get<ApplicationDbContext>();
fixes the issue
I had the same problem and this is how I fixed it.
Delete your current database (I was using .mdf)
Create a new database
Clean and rebuild
Update database - if needed -
Unfortunately, I do not know the source of the problem. Please edit the answer/post a comment if you know :)
For other people with the same issue, make sure the username/password in ApplicationDbInitializer are valid. It will hang if you set the admin password to 123 for example.
Edit: This post provides an explanation + answer
http://forums.asp.net/post/5802779.aspx
Suhas Joshi has the correct answer. The ApplicationDbContext object should be in essence a singleton that is managed by the "Microsoft ASPNET Identity Owin" package (installed using NuGet). When you manually create a new instance of it in your ApplicationController there is contention for the same resource causing this deadlock.
Note that the "Get()" extension used comes from the following library which you must have a reference to in your ApplicationContext class.
using Microsoft.AspNet.Identity.Owin;
Thanks Suhas Joshi for pointing this out as it saved me greatly. I would have just up-voted your answer, but unfortunately I don't have a strong enough reputation yet :).
I have a small webapp that uses EntityFramework to store stuff via repositories into the database.
What I've done so far (based on all the tutorials I read) is create a repository where I need it, as shown below:
In CustomMembershipProvider:
public CustomMembershipProvider()
{
_userRepository = new UserRepository(new TenantApplicationContext());
}
In my RegisterController:
public TenantRepository TenantRepository { get; set; }
public UserRepository UserRepository { get; set; }
protected override void Initialize(RequestContext requestContext)
{
if (MembershipService == null) { MembershipService = new AccountMembershipService(); }
if (TenantRepository == null) { TenantRepository = new TenantRepository(TenantApplicationContext); }
if (UserRepository == null) { UserRepository = new UserRepository(TenantApplicationContext); }
base.Initialize(requestContext);
}
The point is, that I instantiate the UserRepository twice. This becomes a problem when I create a User in one instance, and try to retrieve it in the other instance, and I did not call SaveChanges in between.
The problem lies here:
// Snippet from the register controller class
if (!UserRepository.Exists(model.AccountableEmailAddress))
{
// 1 - Create the user via a custom MembershipProvider
// Note, the CustomMembershipProvider has it's own instance of UserRepository
var createStatus = MembershipService.CreateUser(
model.AccountableUser,
model.Password,
model.AccountableEmailAddress);
if (createStatus == MembershipCreateStatus.Success)
{
// Left out irrelevant code
AdministerUserAndTenant(tenant.Name, model.AccountableEmailAddress);
}
}
private void AdministerUserAndTenant(string tenantName, string emailAddress)
{
// 2 - Try to retrieve the user from a different (!) instance of UserRepository
var user = UserRepository.GetUser(emailAddress);
var tenant = TenantRepository.GetTenantByName(tenantName);
tenant.Users.Add(user);
TenantApplicationContext.SaveChanges();
}
I hope you can still follow, tried to leave out unnecessary parts.
What is the best way to deal with issues like this?
PS: I'm not very fond of the Singleton pattern, so if possible don't go there :).
When exactly does it become a problem? Cause that's where the answer lies. Classes that should know of each other's unsaved changes should use the same repository instance. Since they are probably related, you'll manage passing a reference between them.
If there's reason why all of your application should have one single repository, use Dependency Injection.
I am not a regular with the MVVM pattern and this is basically my first time playing with it.
What I used to do ("normal" WPF), was creating my Views with a Business layer and perhaps a datalayer (which usually contains my entities created by a service or the Entity Framework).
Now after some toying I created a standard template from MVVM Light and did this:
Locator:
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IUserService, DesignUserService>();
}
else
{
SimpleIoc.Default.Register<IUserService, IUserService>();
}
SimpleIoc.Default.Register<LoginViewModel>();
}
public LoginViewModel Login
{
get
{
return ServiceLocator.Current.GetInstance<LoginViewModel>();
}
}
}
Login ViewModel:
public class LoginViewModel : ViewModelBase
{
private readonly IUserService _userService;
public RelayCommand<Object> LoginCommand
{
get
{
return new RelayCommand<Object>(Login);
}
}
private string _userName;
public String UserName
{
get { return _userName; }
set
{
if (value == _userName)
return;
_userName = value;
RaisePropertyChanged("UserName");
}
}
/// <summary>
/// Initializes a new instance of the LoginViewModel class.
/// </summary>
public LoginViewModel(IUserService userService)
{
_userService = userService;
_closing = true;
}
private void Login(Object passwordBoxObject)
{
PasswordBox passwordBox = passwordBoxObject as PasswordBox;
if (passwordBox == null)
throw new Exception("PasswordBox is null");
_userService.Login(UserName, passwordBox.SecurePassword, result =>
{
if (!result)
{
MessageBox.Show("Wrong username or password");
}
});
}
}
Binding and commands work fine so there is no questions. Business mockup class for design and test time:
public class DesignUserService : IUserService
{
private readonly User _testUser;
private readonly IList<User> _users;
public void Login(String userName, SecureString password, Action<Boolean> callback)
{
var user = _users.FirstOrDefault(u => u.UserName.ToLower() == userName.ToLower());
if (user == null)
{
callback(false);
return;
}
String rawPassword = Security.ComputeHashString(password, user.Salt);
if (rawPassword != user.Password)
{
callback(false);
return;
}
callback(true);
}
public DesignUserService()
{
_testUser = new User
{
UserName = "testuser",
Password = "123123",
Salt = "123123"
};
_users = new List<User>
{
_testUser
};
}
}
UserData is a static class which makes calls to the database (Entity Framework).
Now I have my test:
[TestClass]
public class Login
{
[TestMethod]
public void IncorrectUsernameCorrectPassword()
{
IUserService userService = new DesignUserService();
PasswordBox passwordBox = new PasswordBox
{
Password = "password"
};
userService.Login("nonexistingusername", passwordBox.SecurePassword, b => Assert.AreEqual(b, false));
}
}
Now my test is not on the ViewModel itself but directly to the Business layer.
Basically I have 2 questions:
Am I on the right path, or is there a fundamental flaw in my pattern implementation?
How can I test my ViewModel?
Your view model has one relevant piece of code worth testing, which is Login method. Given that it's private, it should be tested it via LoginCommand.
Now, one might ask, what is the purpose of testing command when you already have test for underlying business logic? The purpose is to verify that business logic is called and with correct parameters.
How do one goes with such test? By using mock. Example with FakeItEasy:
var userServiceFake = A.Fake<IUserService>();
var testedViewModel = new LoginViewModel(userServiceFake);
// prepare data for test
var passwordBox = new PasswordBox { Password = "password" };
testedViewModel.UserName = "TestUser";
// execute test
testedViewModel.LoginCommand.Execute(passwordBox);
// verify
A.CallTo(() => userServiceFake.Login(
"TestUser",
passwordBox.SecurePassword,
A<Action<bool>>.Ignored)
).MustHaveHappened();
This way you verify that command calls business layer as expected. Note that Action<bool> is ignored when matching parameters - it's difficult to match Action<T> and Func<T> and usually not worth it.
Few notes:
You might want to reconsider having message box code in view model (this should belong to view, view model should either request or notify view to display popup). Doing so, will also make more through testing of view model possible (eg. not needing to ignore that Action argument)
Some people do test INotifyPropertyChanged properties (UserName in your case) - that event is raised when property value changes. Since this is lot of boilerplate code, using tool /library to automate this process is highly suggested.
You do want to have two sets of tests, one for view model (as in the example above) and one for underlying business logic (your original test). In MVVM, VM is that extra layer which might seem to be of little use - but that's the whole point - to have no business logic there and rather focus data rearrangement/preparation for views layer.