public void NoUserTypeIdUpdate() { var r = new Restrictions(); var user = new User { Id = 1, UserTypeId = 2 }; var updates = new Dictionary<object, Restrictions.ShadowModifiedMember[]>(); updates[user] = new Restrictions.ShadowModifiedMember[] { new Restrictions.ShadowModifiedMember { Member = typeof(User).GetProperty("UserTypeId"), CurrentValue = user.UserTypeId, OriginalValue = 1 } }; string ignored; Assert.IsFalse(r.IsValidChangeSet(updates, new List<object>(), new List<object>(), new List<object>() { user }, new List<int>() { 1 }, out ignored)); Assert.IsFalse(r.IsValidChangeSet(updates, new List<object>(), new List<object>(), new List<object>() { user }, new List<int>(), out ignored)); }
public void UpdateUsersInList() { var r = new Restrictions(); var user = new User { Id = 1, CreationDate = DateTime.UtcNow }; var updates = new Dictionary<object, Restrictions.ShadowModifiedMember[]>(); updates[user] = new Restrictions.ShadowModifiedMember[] { new Restrictions.ShadowModifiedMember { Member = typeof(User).GetProperty("CreationDate"), CurrentValue = user.CreationDate, OriginalValue = null } }; string ignored; Assert.IsTrue(r.IsValidChangeSet(updates, new List<object>(), new List<object>(), new List<object>() { user }, new List<int>() { 1 }, out ignored), ignored); }
/// <summary> /// Repeated logic from SetNewPassword. /// /// Pass it a resetToken to destroy on success (if user is anonymous). /// /// Returns null if everything is OK, and an ActionResult if an error occurred. /// </summary> private ActionResult ChangePasswordAndSendEmail(string password, string password2, string token, PasswordReset resetToken, User user, DateTime now) { string message; if (!Password.CheckPassword(password, password2, user.Email, user.VanityProviderId, user.ProviderId, out message)) return RecoverableError(message, new { token }); user.ChangePassword(now, password); if (resetToken != null) { Current.WriteDB.PasswordResets.DeleteOnSubmit(resetToken); } Current.WriteDB.SubmitChanges(); var account = SafeRedirect((Func<ActionResult>)(new UserController()).ViewUser); if (!Current.Email.SendEmail(user.Email, Email.Template.PasswordChanged, new { AccountLink = Current.Url(account.Url).AsLink() })) { return IrrecoverableError("An error occurred sending the email", "This has been recorded, and will be looked into shortly"); } return null; }
partial void DeleteUser(User instance);
partial void UpdateUser(User instance);
partial void InsertUser(User instance);
/// <summary> /// Gives a black mark to an IP that just failed a login attempt. /// /// Passes the user, if the account exists at all. /// /// Repeated attempts to login to a single account can be very worrying past /// a certain number, but fat fingering does happen so we don't want to panic /// immediately. /// /// Repeated attempts to *different* accounts is almost certainly a sign of an /// attack, once the number of involved accounts grows to a certain size. /// </summary> public static void BadLoginAttempt(User user, string ip) { UpdateInfractionList(ip, new Infraction { Type = Infraction.InfractionType.Login, Expires = Current.Now.Add(TimeSpan.FromMinutes(5)), RelatedId = user != null ? user.Id : -1 } ); var existingInfractions = GetInfractionList(ip); var singleUser = existingInfractions.Where(i => i.Type == Infraction.InfractionType.Login && i.RelatedId != -1).GroupBy(i => i.RelatedId).Max(g => (int?)g.Count()); if (singleUser > 10) { Ban(ip, TimeSpan.FromMinutes(5), "More than 10 attempts to login as a user."); return; } var noUser = existingInfractions.Count(i => i.Type == Infraction.InfractionType.Login && i.RelatedId == -1); if (noUser > 20) { Ban(ip, TimeSpan.FromMinutes(30), "More than 20 attempts to login."); return; } var total = existingInfractions.Count(i => i.Type == Infraction.InfractionType.Login); // This suggests they're trying to actually dodge our single and scan behavior; drop the hammer if (total > 30) { Ban(ip, TimeSpan.FromMinutes(60), "Appears to be spamming login attempts, while dodging throttles."); return; } }
/// <summary> /// Create a new account given an email and password /// </summary> public static bool CreateAccount(string email, PendingUser pendingUser, DateTime now, string vanity, string realname, out User created, out string errorMessage) { email = email.ToLowerInvariant(); if (vanity.HasValue() && !Models.User.IsValidVanityId(vanity, out errorMessage)) { created = null; return false; } var db = Current.WriteDB; // Quick check to make sure the vanity id is not in use elsewhere if (vanity.HasValue() && db.Users.Any(u => u.VanityProviderId == vanity)) { created = null; errorMessage = "That Vanity OpenId is already in use"; return false; } var provider = Current.UniqueId(); // Odds of colision are miniscule, but might as well check while (db.Users.Any(u => u.ProviderId == provider)) provider = Current.UniqueId(); // We need to compute these way before we use them for some length checks byte emailVersion; string emailIV, emailHMAC; var emailEncrypted = Current.Encrypt(email, out emailIV, out emailVersion, out emailHMAC); byte nameVersion = 0xFF; string nameEncrypted= null, nameIV = null, nameHMAC = null; if (realname.HasValue()) { nameEncrypted = Current.Encrypt(realname, out nameIV, out nameVersion, out nameHMAC); } if (emailEncrypted.Length > 267) { created = null; errorMessage = "Email is too long"; return false; } if (nameEncrypted.HasValue() && nameEncrypted.Length > 267) { created = null; errorMessage = "Name is too long"; return false; } string emailHash; byte emailSaltVersion; emailHash = Current.SystemHash(email, out emailSaltVersion); var newUser = new User { LastActivityDate = DateTime.UtcNow, EmailHash = emailHash, EmailSaltVersion = emailSaltVersion, ProviderId = provider, PasswordHash = pendingUser.PasswordHash, PasswordSalt = pendingUser.PasswordSalt, CreationDate = now, VanityProviderId = vanity, UserTypeId = Models.UserTypeId.Normal }; try { db.Users.InsertOnSubmit(newUser); db.SubmitChanges(); } catch (Exception e) { // Hack: There isn't really a nice way to detect a unique constraint conflict, // so... check the message. Checking for the constraint name so this isn't // *guaranteed* to break on non-English language systems... still not guaranteed // to work though. if(e is System.Data.SqlClient.SqlException && e.Message.Contains("Users_EmailHash_EmailSaltVersion")) { created = null; errorMessage = "Email address already registered."; return false; } Current.LogException(e); created = null; errorMessage = "User account could not be created."; return false; } // Open season on this user until the context is torn down. db.LiftUserRestrictionsOnId = newUser.Id; // Can't put a unique constrain on VanityProviderId (as its normally null), so // this is a hack to make sure no two users end up slipping in and getting the // same vanity id. if (vanity.HasValue() && db.Users.Count(u => u.VanityProviderId == vanity) != 1) { newUser.VanityProviderId = null; db.SubmitChanges(); } var emailAttr = new UserAttribute { UserId = newUser.Id, CreationDate = now, UserAttributeTypeId = UserAttributeTypeId.Email, Encrypted = emailEncrypted, IV = emailIV, KeyVersion = emailVersion, HMAC = emailHMAC }; db.UserAttributes.InsertOnSubmit(emailAttr); db.SubmitChanges(); if (realname.HasValue()) { var nameAttr = new UserAttribute { UserId = newUser.Id, CreationDate = now, UserAttributeTypeId = UserAttributeTypeId.RealName, Encrypted = nameEncrypted, IV = nameIV, KeyVersion = nameVersion, HMAC = nameHMAC }; db.UserAttributes.InsertOnSubmit(nameAttr); db.SubmitChanges(); } created = newUser; errorMessage = null; return true; }