partial void UpdateUserAttribute(UserAttribute instance);
partial void DeleteUserAttribute(UserAttribute instance);
partial void InsertUserAttribute(UserAttribute instance);
/// <summary> /// Update an existing attribute to the same value (provided for performance sake) but with the latest key version. /// /// Requires a subsequent call to SubmitChanges on Current.WriteDB /// </summary> public bool UpdateAttribute(string value, byte attribute, out string message) { using (MiniProfiler.Current.Step("UpdateAttribute")) { var db = Current.WriteDB; var toUpdate = (from user in db.Users join attr in db.UserAttributes on user.Id equals attr.UserId where attr.UserAttributeTypeId == attribute && user.Id == this.Id select attr).SingleOrDefault(); if (value.IsNullOrEmpty()) { if (toUpdate != null) { db.UserAttributes.DeleteOnSubmit(toUpdate); } message = null; return true; } if (toUpdate == null) { toUpdate = new UserAttribute(); toUpdate.CreationDate = Current.Now; toUpdate.UserId = this.Id; toUpdate.UserAttributeTypeId = attribute; db.UserAttributes.InsertOnSubmit(toUpdate); } string iv, hmac; byte version; var updated = Current.Encrypt(value, out iv, out version, out hmac); if (updated.Length > 267) { message = UserAttributeTypeId.GetDisplayName(attribute) + " is too long."; return false; } toUpdate.Encrypted = updated; toUpdate.IV = iv; toUpdate.KeyVersion = version; toUpdate.HMAC = hmac; message = null; return true; } }
/// <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; }
public void UserAttributeInsertWithUserId() { var r = new Restrictions(); var attr = new UserAttribute { UserId = 1 }; var updates = new Dictionary<object, Restrictions.ShadowModifiedMember[]>(); string ignored; Assert.IsTrue(r.IsValidChangeSet(updates, new List<object>(), new List<object>() { attr }, new List<object>(), new List<int>() { 1 }, out ignored)); }
public void UserAttributeUpdateWithUserId() { var r = new Restrictions(); var attr = new UserAttribute { UserId = 1 }; var updates = new Dictionary<object, Restrictions.ShadowModifiedMember[]>(); updates[attr] = new Restrictions.ShadowModifiedMember[] { new Restrictions.ShadowModifiedMember { Member = typeof(UserAttribute).GetProperty("UserId"), CurrentValue = attr.Id, OriginalValue = 2 } }; string ignored; Assert.IsTrue(r.IsValidChangeSet(updates, new List<object>(), new List<object>(), new List<object>() { attr }, new List<int>() { 1 }, out ignored)); }