public InternalCreateAccountEmailViewModel(User user, CreateAccountViewModel registrationData) { this.RegistrationData = registrationData.Clone(); this.RegistrationData.Password = "******"; this.RegistrationData.ConfirmPassword = "******"; this.UrlIdentifier = user.Practice.UrlIdentifier; this.UserName = user.UserName; }
/// <summary> /// Creates a new user and adds it to the storage object context. /// </summary> /// <param name="createdUser">Output paramater that returns the new user.</param> /// <param name="registrationData">Object containing informations about the user to be created.</param> /// <param name="dbUserSet">Storage object context used to add the new user. It won't be saved, just changed.</param> /// <param name="utcNow"> </param> /// <param name="practiceId">The id of the practice that the new user belongs to.</param> /// <returns>An enumerated value indicating what has happened.</returns> public static CreateUserResult CreateUser(out User createdUser, CreateAccountViewModel registrationData, IObjectSet<User> dbUserSet, DateTime utcNow, int? practiceId) { // Password cannot be null, nor empty. if (string.IsNullOrEmpty(registrationData.Password)) { createdUser = null; return CreateUserResult.InvalidUserNameOrPassword; } // User-name cannot be null, nor empty. if (string.IsNullOrEmpty(registrationData.UserName)) { createdUser = null; return CreateUserResult.InvalidUserNameOrPassword; } // Password salt and hash. string passwordSalt = CipherHelper.GenerateSalt(); var passwordHash = CipherHelper.Hash(registrationData.Password, passwordSalt); // Normalizing user name. // The normalized user-name will be used to discover if another user with the same user-name already exists. // This is a security measure. This makes it very difficult to guess what a person's user name may be. // You can only login with the exact user name that you provided the first timestamp, // but if someone tries to register a similar user name just to know if that one is the one you used... // the attacker won't be sure... because it could be any other variation. // e.g. I register user-name "Miguel.Angelo"... the attacker tries to register "miguelangelo", he'll be denied... // but that doesn't mean the exact user-name "miguelangelo" is the one I used, in fact it is not. var normalizedUserName = StringHelper.NormalizeUserName(registrationData.UserName); var isUserNameAlreadyInUse = practiceId != null && dbUserSet.Any(u => u.UserNameNormalized == normalizedUserName && u.PracticeId == practiceId); if (isUserNameAlreadyInUse) { createdUser = null; return CreateUserResult.UserNameAlreadyInUse; } // Creating user. createdUser = new User { Person = new Person { // Note: DateOfBirth property cannot be set in this method because of Utc/Local conversions. // The caller of this method must set the property. Gender = registrationData.Gender ?? 0, FullName = registrationData.FullName, CreatedOn = utcNow, Email = registrationData.EMail, EmailGravatarHash = GravatarHelper.GetGravatarHash(registrationData.EMail), }, UserName = registrationData.UserName, UserNameNormalized = normalizedUserName, PasswordSalt = passwordSalt, Password = passwordHash, SYS_PasswordAlt = null, LastActiveOn = utcNow, }; if (practiceId != null) { createdUser.PracticeId = (int)practiceId; createdUser.Person.PracticeId = (int)practiceId; } dbUserSet.AddObject(createdUser); return CreateUserResult.Ok; }
public void CreateAccount_2_PracticeNameThatAlreadyExists() { using (var disposer = new Disposer()) { AuthenticationController controller; var hasBeenSaved = false; CreateAccountViewModel vm; try { var mr = new MockRepository(); var mve = mr.SetupViewEngine(disposer); mve.SetViewContent( "ConfirmationEmail", vc => vc.ViewData.Model.ConvertObjectToString("<div>{0}={1}</div>")); mr.SetupHttpContext(disposer); controller = mr.CreateController<AuthenticationController>( setupNewDb: db2 => db2.SavingChanges += (s, e) => { hasBeenSaved = true; }); var practiceName = this.db.Practices.Single().UrlIdentifier; controller.EmailSender = mm => { // Do nothing, instead of sending a REAL e-mail. // Don't need to test if this has been called... another test already does this. }; vm = new CreateAccountViewModel { UserName = "******", PracticeName = practiceName, Password = "******", ConfirmPassword = "******", DateOfBirth = new DateTime(1984, 05, 04), EMail = "*****@*****.**", FullName = "Miguel Angelo Santos Bicudo", Gender = (short)TypeGender.Male, }; Mvc3TestHelper.SetModelStateErrors(controller, vm); } catch (Exception ex) { InconclusiveInit(ex); return; } // Creating a new user without an e-mail. // This must be ok, no exceptions, no validation errors. ActionResult actionResult; { actionResult = controller.CreateAccount(vm); } // Assertions. Assert.IsNotNull(actionResult, "The result of the controller method is null."); Assert.IsInstanceOfType(actionResult, typeof(ViewResult)); var viewResult = (ViewResult)actionResult; Assert.AreEqual(viewResult.ViewName, ""); Assert.IsFalse(controller.ModelState.IsValid, "ModelState should not be valid."); Assert.AreEqual(1, controller.ModelState.GetAllErrors().Count, "ModelState should contain one validation message."); Assert.IsTrue( controller.ModelState.ContainsKey("PracticeName"), "ModelState must contain validation message for 'PracticeName'."); Assert.IsFalse(hasBeenSaved, "The database has been changed. This was not supposed to happen."); } }
public void CreateAccount_WithDoctor_HappyPath() { using (var disposer = new Disposer()) { AuthenticationController controller; var hasBeenSaved = false; CreateAccountViewModel vm; try { var mr = new MockRepository(); var mve = mr.SetupViewEngine(disposer); mve.SetViewContent( "ConfirmationEmail", vc => vc.ViewData.Model.ConvertObjectToString("<div>{0}={1}</div>")); mr.SetRouteData_ControllerAndActionOnly("Home", "Index"); mr.SetupHttpContext(disposer); controller = mr.CreateController<AuthenticationController>( setupNewDb: db2 => db2.SavingChanges += (s, e) => { hasBeenSaved = true; }); mr.SetupUrlHelper(controller); controller.EmailSender = mm => { // Do nothing, instead of sending a REAL e-mail. // Don't need to test if this has been called... another test already does this. }; // Creating ViewModel, and setting the ModelState of the controller. var me = Firestarter.GetMedicalEntity_Psicologia(this.db); var ms = Firestarter.GetMedicalSpecialty_Psiquiatra(this.db); vm = new CreateAccountViewModel { UserName = "******", PracticeName = "consultoriodrhouse_08sd986", Password = "******", ConfirmPassword = "******", DateOfBirth = new DateTime(1984, 05, 04), EMail = "*****@*****.**", FullName = "André", Gender = (short)TypeGender.Male, IsDoctor = true, MedicCRM = "98237", MedicalEntityId = me.Id, MedicalSpecialtyId = ms.Id, MedicalSpecialtyName = ms.Name, MedicalEntityJurisdiction = (int)TypeEstadoBrasileiro.RJ, }; Mvc3TestHelper.SetModelStateErrors(controller, vm); } catch (Exception ex) { InconclusiveInit(ex); return; } // Creating a new user without an e-mail. // This must be ok, no exceptions, no validation errors. ActionResult actionResult; { actionResult = controller.CreateAccount(vm); } // Getting the user that was saved. var savedUser = this.db.Users.Single(u => u.UserName == "andré-01"); // Assertions. Assert.IsNotNull(actionResult, "The result of the controller method is null."); Assert.IsInstanceOfType(actionResult, typeof(RedirectToRouteResult)); var redirectResult = (RedirectToRouteResult)actionResult; Assert.AreEqual(redirectResult.RouteValues["action"], "CreateAccountCompleted"); Assert.IsTrue(controller.ModelState.IsValid, "ModelState should be valid."); Assert.IsTrue(hasBeenSaved, "The database should be changed, but it was not."); Assert.AreEqual(savedUser.UserNameNormalized, "andre01"); // Assert user is logged-in: this is already done in CreateAccount_HappyPath. // Assertion for email: this is already done in CreateAccount_HappyPath. } }
public void CreateAccount_HappyPath() { using (var disposer = new Disposer()) { AuthenticationController controller; var hasBeenSaved = false; CreateAccountViewModel vm; var wasEmailSent = false; string emailBody = null, emailSubject = null, emailToAddress = null; var utcNow = new DateTime(2012, 08, 31, 0, 0, 0, DateTimeKind.Utc); try { var mr = new MockRepository(); var mve = mr.SetupViewEngine(disposer); mve.SetViewContent( "ConfirmationEmail", vc => vc.ViewData.Model.ConvertObjectToString("<div>{0}={1}</div>")); mr.SetRouteData_ControllerAndActionOnly("Home", "Index"); mr.SetupHttpContext(disposer); controller = mr.CreateController<AuthenticationController>( setupNewDb: db2 => db2.SavingChanges += (s, e) => { hasBeenSaved = true; }); controller.UtcNowGetter = () => utcNow; controller.EmailSender = mm => { wasEmailSent = true; emailBody = mm.Body; emailSubject = mm.Subject; emailToAddress = mm.To.Single().Address; }; // Creating ViewModel, and setting the ModelState of the controller. vm = new CreateAccountViewModel { UserName = "******", PracticeName = "consultoriodrhouse_08sd986", Password = "******", ConfirmPassword = "******", DateOfBirth = new DateTime(1984, 05, 04), EMail = "*****@*****.**", FullName = "André", Gender = (short)TypeGender.Male, }; Mvc3TestHelper.SetModelStateErrors(controller, vm); } catch (Exception ex) { InconclusiveInit(ex); return; } // Creating a new user without an e-mail. // This must be ok, no exceptions, no validation errors. ActionResult actionResult; { actionResult = controller.CreateAccount(vm); } // Getting the user that was saved. var savedUser = this.db.Users.Single(u => u.UserName == "andré-01"); var savedToken = this.db.GLB_Token.Single(); // Assertions. Assert.IsNotNull(actionResult, "The result of the controller method is null."); Assert.IsInstanceOfType(actionResult, typeof(RedirectToRouteResult)); var redirectResult = (RedirectToRouteResult)actionResult; Assert.AreEqual(redirectResult.RouteValues["action"], "CreateAccountCompleted"); Assert.IsTrue(controller.ModelState.IsValid, "ModelState should be valid."); Assert.IsTrue(hasBeenSaved, "The database should be changed, but it was not."); // Assert DB values. Assert.AreEqual(savedUser.UserNameNormalized, "andre01"); Assert.AreEqual(32, savedToken.Value.Length); Assert.AreEqual(savedUser.Practice.VerificationDate, null); Assert.AreEqual(utcNow.AddDays(30), savedToken.ExpirationDate); Assert.IsTrue(savedUser.IsOwner, "Saved user should be the owner of the practice."); Assert.AreEqual(savedUser.Id, savedUser.Practice.OwnerId, "Saved user should be the owner of the practice."); Assert.IsNotNull(savedUser.Administrator, "Practice owner must be administrator."); // Assert user is logged-in. Assert.IsTrue( controller.HttpContext.Response.Cookies.Keys.Cast<string>().Contains(".ASPXAUTH"), "Authentication cookie should be present in the Response."); var authCookie = controller.HttpContext.Response.Cookies[".ASPXAUTH"]; Assert.IsNotNull(authCookie, @"Response.Cookies["".ASPXAUTH""] must not be null."); var ticket = System.Web.Security.FormsAuthentication.Decrypt(authCookie.Value); Assert.AreEqual("andré-01", ticket.Name); var token = SecurityTokenHelper.FromString(ticket.UserData); Assert.AreEqual(savedUser.Id, token.UserData.Id); Assert.AreEqual("André", token.UserData.FullName); Assert.AreEqual("*****@*****.**", token.UserData.Email); Assert.AreEqual(false, token.UserData.IsUsingDefaultPassword); // Assertion for email. Assert.IsTrue(wasEmailSent, "E-mail was not sent, but it should."); var emailViewModel = new UserEmailViewModel(savedUser) { Token = new TokenId(savedToken.Id, savedToken.Value).ToString(), }; var emailExpected = emailViewModel.ConvertObjectToString("<div>{0}={1}</div>"); Assert.AreEqual(emailExpected, emailBody); Assert.AreEqual("Bem vindo ao Cerebello! Por favor, confirme a criação de sua conta.", emailSubject); Assert.AreEqual("*****@*****.**", emailToAddress); } }
public void CreateAccount_4_UserNameIsInvalid() { using (var disposer = new Disposer()) { AuthenticationController controller; var hasBeenSaved = false; CreateAccountViewModel vm; var hasEmail = false; try { var mr = new MockRepository(); mr.SetRouteData(typeof(AuthenticationController), "CreateAccount"); var mve = mr.SetupViewEngine(disposer); mve.SetViewContent("ConfirmationEmail", vc => "<html>Test e-mail string.</html>"); controller = mr.CreateController<AuthenticationController>( setupNewDb: db2 => db2.SavingChanges += (s, e) => { hasBeenSaved = true; }); controller.EmailSender = mm => { hasEmail = true; }; // Creating ViewModel, and setting the ModelState of the controller. vm = new CreateAccountViewModel { UserName = "******", // char # is invalid PracticeName = "New Practice Name 4146", Password = "******", ConfirmPassword = "******", DateOfBirth = new DateTime(1984, 09, 01), EMail = "*****@*****.**", FullName = "André Rodrigues Pena", Gender = (short)TypeGender.Male, }; Mvc3TestHelper.SetModelStateErrors(controller, vm); } catch (Exception ex) { InconclusiveInit(ex); return; } // Creating a new user without an e-mail. // This must be ok, no exceptions, no validation errors. ActionResult actionResult; { actionResult = controller.CreateAccount(vm); } // Assertions. Assert.IsNotNull(actionResult, "The result of the controller method is null."); Assert.IsInstanceOfType(actionResult, typeof(ViewResult)); var viewResult = (ViewResult)actionResult; Assert.AreEqual(viewResult.ViewName, ""); Assert.IsFalse(controller.ModelState.IsValid, "ModelState should not be valid."); Assert.AreEqual(1, controller.ModelState.GetAllErrors().Count, "ModelState should contain one validation message."); Assert.IsTrue( controller.ModelState.ContainsKey("UserName"), "ModelState must contain validation message for 'PracticeName'."); Assert.IsFalse(hasBeenSaved, "The database has been changed. This was not supposed to happen."); Assert.IsFalse(hasEmail, "A confirmation e-mail has been sent. This was not supposed to happen."); } }
/// <summary> /// Simulates the creation of a new account, /// by using the real controller, /// and mocking everything that is of no interest. /// </summary> /// <param name="utcNow"></param> /// <param name="password"> </param> /// <param name="outToken"> </param> private static int CreateAccount_Helper(DateTime utcNow, out string password, out string outToken) { using (var disposer = new Disposer()) { var mr = new MockRepository(); string token = null; var mve = mr.SetupViewEngine(disposer); mve.SetViewContent( "ConfirmationEmail", vc => { token = ((UserEmailViewModel)vc.ViewData.Model).Token; return "Fake e-mail message."; }); mr.SetupHttpContext(disposer); var controller = mr.CreateController<AuthenticationController>(); controller.UtcNowGetter = () => utcNow; controller.EmailSender = mm => { // Just don't send any REAL e-mail. }; // Creating ViewModel, and setting the ModelState of the controller. password = "******"; var vm = new CreateAccountViewModel { UserName = "******", PracticeName = "consultoriodrhouse_08sd986", Password = password, ConfirmPassword = password, DateOfBirth = new DateTime(1984, 05, 04), EMail = "*****@*****.**", FullName = "André", Gender = (short)TypeGender.Male, }; Mvc3TestHelper.SetModelStateErrors(controller, vm); // Call the action on the controller to create the new account. // No assertions will be made to this, because this is not a test. // If you want to test any values, do it in a TEST METHOD. controller.CreateAccount(vm); outToken = token; // Getting the Id of the user that was created, and returning it. var authCookie = controller.HttpContext.Response.Cookies[".ASPXAUTH"]; Assert.IsNotNull(authCookie, @"Response.Cookies["".ASPXAUTH""] must not be null."); var ticket = System.Web.Security.FormsAuthentication.Decrypt(authCookie.Value); var securityToken = SecurityTokenHelper.FromString(ticket.UserData); return securityToken.UserData.Id; } }
public void CreateAccount_3_UserNameExistsInAnotherPractice_HappyPath() { using (var disposer = new Disposer()) { AuthenticationController controller; var hasBeenSaved = false; CreateAccountViewModel vm; try { var mr = new MockRepository(); var mve = mr.SetupViewEngine(disposer); mve.SetViewContent( "ConfirmationEmail", vc => vc.ViewData.Model.ConvertObjectToString("<div>{0}={1}</div>")); mr.SetRouteData_ControllerAndActionOnly("Home", "Index"); mr.SetupHttpContext(disposer); controller = mr.CreateController<AuthenticationController>( setupNewDb: db2 => db2.SavingChanges += (s, e) => { hasBeenSaved = true; }); var userFullName = this.db.Users.Single().Person.FullName; var userName = this.db.Users.Single().UserName; controller.EmailSender = mm => { // Do nothing, instead of sending a REAL e-mail. // Don't need to test if this has been called... another test already does this. }; vm = new CreateAccountViewModel { UserName = userName, PracticeName = "consultoriodrhouse_0832986", Password = "******", ConfirmPassword = "******", DateOfBirth = new DateTime(1984, 05, 04), EMail = "*****@*****.**", FullName = userFullName, Gender = (short)TypeGender.Male, }; Mvc3TestHelper.SetModelStateErrors(controller, vm); } catch (Exception ex) { InconclusiveInit(ex); return; } // Creating a new user without an e-mail. // This must be ok, no exceptions, no validation errors. ActionResult actionResult; { actionResult = controller.CreateAccount(vm); } // Assertions. Assert.IsNotNull(actionResult, "The result of the controller method is null."); Assert.IsInstanceOfType(actionResult, typeof(RedirectToRouteResult)); var redirectResult = (RedirectToRouteResult)actionResult; Assert.AreEqual(redirectResult.RouteValues["action"], "CreateAccountCompleted"); Assert.IsTrue(controller.ModelState.IsValid, "ModelState should be valid."); Assert.IsTrue(hasBeenSaved, "The database should be changed, but it was not."); } }
public ActionResult CreateAccount(string subscription) { subscription = StringHelper.FirstNonEmpty(subscription, "trial"); if (!validSubscriptions.Contains(subscription)) subscription = "trial"; this.ViewBag.MedicalSpecialtyOptions = this.db.SYS_MedicalSpecialty .ToList() .Select(me => new SelectListItem { Value = me.Id.ToString(), Text = me.Name }) .ToList(); this.ViewBag.MedicalEntityOptions = this.db.SYS_MedicalEntity .ToList() .Select(me => new SelectListItem { Value = me.Id.ToString(), Text = me.Name }) .ToList(); var registrationData = new CreateAccountViewModel { Subscription = subscription, }; return this.View(registrationData); }
public ActionResult PayPalConfirmed(string practice) { var utcNow = this.GetUtcNow(); // Creating confirmation email, with a token. using (var emailMessageToUser = this.EmailMessageToUser(this.dbUser, utcNow, isTrial: false)) { var createAccountViewModel = new CreateAccountViewModel { DateOfBirth = this.dbUser.Person.DateOfBirth, PracticeName = this.dbPractice.Name, PracticePhone = this.dbPractice.PhoneMain, Password = "", Subscription = string.Format( "{0}{1}", this.dbPractice.AccountContract.BillingPeriodSize, this.dbPractice.AccountContract.BillingPeriodType), UserName = this.dbUser.UserName, ConfirmPassword = "", EMail = this.dbUser.Person.Email, AsTrial = null, FullName = this.dbUser.Person.FullName, Gender = this.dbUser.Person.Gender, IsDoctor = this.dbUser.DoctorId.HasValue, PracticeProvince = this.dbPractice.Province, }; var doctor = this.dbUser.Doctor; if (doctor != null) { var medicalEntity = UsersController.GetDoctorEntity(this.db.SYS_MedicalEntity, doctor); var medicalSpecialty = UsersController.GetDoctorSpecialty(this.db.SYS_MedicalSpecialty, doctor); // Getting all patients data. var viewModel = new UserViewModel(); UsersController.FillDoctorViewModel(this.dbUser, medicalEntity, medicalSpecialty, viewModel, doctor); createAccountViewModel.MedicCRM = viewModel.MedicCRM; createAccountViewModel.MedicalEntityId = viewModel.MedicalEntityId; createAccountViewModel.MedicalEntityJurisdiction = viewModel.MedicalEntityJurisdiction; createAccountViewModel.MedicalSpecialtyId = viewModel.MedicalSpecialtyId; createAccountViewModel.MedicalSpecialtyName = viewModel.MedicalSpecialtyName; } this.SendAccountCreatedSelfEmail(createAccountViewModel, this.dbUser); if (emailMessageToUser != null) this.TrySendEmail(emailMessageToUser); } return this.RedirectToAction("Welcome", "Home", new { practice }); }
private void SendAccountCreatedSelfEmail(CreateAccountViewModel registrationData, User user) { // sending e-mail to [email protected] // to tell us the good news // lots of try catch... this is an internal thing, and should never fail to the client, even if it fails try { var emailViewModel2 = new InternalCreateAccountEmailViewModel(user, registrationData); var toAddress2 = new MailAddress("*****@*****.**", registrationData.FullName); var mailMessage2 = this.CreateEmailMessagePartial("InternalCreateAccountEmail", toAddress2, emailViewModel2); this.SendEmailAsync(mailMessage2).ContinueWith( t => { // send e-mail again is not an option, SendEmailAsync already tries a lot of times // observing exception so that it is not raised var ex = t.Exception; Trace.TraceError( string.Format( "AuthenticationController.CreateAccount(CreateAccountViewModel): exception when sending internal e-mail: {0}", TraceHelper.GetExceptionMessage(ex))); }); } catch (Exception ex) { Trace.TraceError( string.Format( "AuthenticationController.CreateAccount(CreateAccountViewModel): exception when sending internal e-mail: {0}", TraceHelper.GetExceptionMessage(ex))); } }
public ActionResult CreateAccount(CreateAccountViewModel registrationData) { // If the user being edited is a medic, then we must check the fields that are required for medics. if (!registrationData.IsDoctor) { // Removing validation error of medic properties, because this user is not a medic. this.ModelState.ClearPropertyErrors(() => registrationData.MedicCRM); this.ModelState.ClearPropertyErrors(() => registrationData.MedicalEntityId); this.ModelState.ClearPropertyErrors(() => registrationData.MedicalSpecialtyId); this.ModelState.ClearPropertyErrors(() => registrationData.MedicalSpecialtyName); this.ModelState.ClearPropertyErrors(() => registrationData.MedicalEntityJurisdiction); } if (this.ModelState.Remove(e => e.ErrorMessage.Contains("requerido"))) this.ModelState.AddModelError("MultipleItems", "É necessário preencher todos os campos."); // Normalizing name properties. if (!string.IsNullOrEmpty(registrationData.PracticeName)) registrationData.PracticeName = Regex.Replace(registrationData.PracticeName, @"\s+", " ").Trim(); if (!string.IsNullOrEmpty(registrationData.FullName)) registrationData.FullName = Regex.Replace(registrationData.FullName, @"\s+", " ").Trim(); var urlPracticeId = StringHelper.GenerateUrlIdentifier(registrationData.PracticeName); var subscription = StringHelper.FirstNonEmpty(registrationData.Subscription, "trial"); if (!validSubscriptions.Contains(subscription) || registrationData.AsTrial == true) subscription = "trial"; // Note: Url identifier for the name of the user, don't need any verification. // The name of the user must be unique inside a practice, not the entire database. var alreadyLoggedUser = this.User as AuthenticatedPrincipal; Practice practiceToReuse = null; if (alreadyLoggedUser != null) { practiceToReuse = this.db.Practices .SingleOrDefault(p => p.UrlIdentifier == alreadyLoggedUser.Profile.PracticeIdentifier && p.AccountContract.IsPartialBillingInfo); } var alreadyExistingPractice = this.db.Practices.SingleOrDefault(p => p.UrlIdentifier == urlPracticeId); if (alreadyExistingPractice != null) { if (alreadyExistingPractice.AccountContract != null && alreadyExistingPractice.AccountContract.IsPartialBillingInfo) practiceToReuse = practiceToReuse ?? alreadyExistingPractice; else { // killing practice that already exists if it expires, and freeing the name for a new practice if (alreadyExistingPractice.AccountExpiryDate < this.GetUtcNow()) { alreadyExistingPractice.AccountDisabled = true; alreadyExistingPractice.UrlIdentifier += " !expired"; // change this, so that a new practice with this name can be created. this.db.SaveChanges(); } else { this.ModelState.AddModelError( () => registrationData.PracticeName, "Nome de consultório já está em uso."); } } } var utcNow = this.GetUtcNow(); // Creating the new user. User user; if (practiceToReuse == null) { var result = SecurityManager.CreateUser(out user, registrationData, this.db.Users, utcNow, null); if (result == CreateUserResult.InvalidUserNameOrPassword) { // Note: nothing to do because user-name and password fields are already validated. } if (result == CreateUserResult.UserNameAlreadyInUse) { this.ModelState.AddModelError( () => registrationData.UserName, // Todo: this message is also used in the App -> UsersController. "O nome de usuário não pode ser registrado pois já está em uso. " + "Note que nomes de usuário diferenciados por acentos, " + "maiúsculas/minúsculas ou por '.', '-' ou '_' não são permitidos." + "(Não é possível cadastrar 'MiguelAngelo' e 'miguel.angelo' no mesmo consultório."); } } else { user = this.db.Users.SingleOrDefault(u => u.Id == alreadyLoggedUser.Profile.Id && u.PracticeId == practiceToReuse.Id); SecurityManager.UpdateUser(user, registrationData, this.db.Users, utcNow); } if (user != null) { string timeZoneId = null; if (registrationData.PracticeProvince != null) timeZoneId = TimeZoneDataAttribute.GetAttributeFromEnumValue((TypeEstadoBrasileiro)registrationData.PracticeProvince.Value).Id; user.Practice = practiceToReuse ?? new Practice(); user.Practice.Name = registrationData.PracticeName; user.Practice.UrlIdentifier = urlPracticeId; user.Practice.CreatedOn = utcNow; user.Practice.WindowsTimeZoneId = timeZoneId; user.Practice.Province = registrationData.PracticeProvince; user.Practice.PhoneMain = registrationData.PracticePhone; // Setting the BirthDate of the user as a person. user.Person.DateOfBirth = PracticeController.ConvertToUtcDateTime(user.Practice, registrationData.DateOfBirth ?? new DateTime()); user.IsOwner = true; if (user.Administrator == null) user.Administrator = new Administrator { }; bool isNewDoctor = false; // when the user is a doctor, we need to fill the properties of the doctor if (registrationData.IsDoctor) { // if user is already a doctor, we just edit the properties // otherwise we create a new doctor instance if (user.Doctor == null) { user.Doctor = new Doctor(); isNewDoctor = true; } user.Doctor.CRM = registrationData.MedicCRM; if (registrationData.MedicalSpecialtyId != null) { var ms = this.db.SYS_MedicalSpecialty .Single(ms1 => ms1.Id == registrationData.MedicalSpecialtyId); user.Doctor.MedicalSpecialtyCode = ms.Code; user.Doctor.MedicalSpecialtyName = ms.Name; } if (registrationData.MedicalEntityId != null) { var me = this.db.SYS_MedicalEntity .Single(me1 => me1.Id == registrationData.MedicalEntityId); user.Doctor.MedicalEntityCode = me.Code; user.Doctor.MedicalEntityName = me.Name; } user.Doctor.MedicalEntityJurisdiction = ((TypeEstadoBrasileiro)registrationData.MedicalEntityJurisdiction).ToString(); // Creating an unique UrlIdentifier for this doctor. // This is the first doctor, so there will be no conflicts. var urlId = UsersController.GetUniqueDoctorUrlId(this.db.Doctors, registrationData.FullName, null); if (urlId == null) { this.ModelState.AddModelError( () => registrationData.FullName, // Todo: this message is also used in the UserController. "Quantidade máxima de homônimos excedida."); } user.Doctor.UrlIdentifier = urlId; } else { // todo: create a program that clears all orphaned Doctor objects user.Doctor = null; } if (practiceToReuse == null) this.db.Users.AddObject(user); if (this.ModelState.IsValid) { MailMessage emailMessageToUser = null; if (subscription == "trial") { // Creating confirmation email, with a token. emailMessageToUser = this.EmailMessageToUser(user, utcNow, subscription == "trial"); // Sending e-mail to tell us the good news. this.SendAccountCreatedSelfEmail(registrationData, user); } // If the ModelState is still valid, then save objects to the database, // and send confirmation email message to the user. using (emailMessageToUser) { // Saving changes to the DB. this.db.SaveChanges(); if (subscription == "trial") { // Creating a new trial account contract. var contract = user.Practice.AccountContract ?? new AccountContract(); contract.Practice = user.Practice; contract.ContractTypeId = (int)ContractTypes.TrialContract; contract.IsTrial = true; contract.IssuanceDate = utcNow; contract.StartDate = utcNow; contract.EndDate = null; // indeterminated contract.CustomText = null; contract.DoctorsLimit = null; contract.PatientsLimit = 50; // fixed limit for trial account // no billings contract.BillingAmount = null; contract.BillingDueDay = null; contract.BillingPaymentMethod = null; contract.BillingPeriodCount = null; contract.BillingPeriodSize = null; contract.BillingPeriodType = null; contract.BillingDiscountAmount = null; user.Practice.AccountExpiryDate = utcNow.AddHours(Constants.MAX_HOURS_TO_VERIFY_TRIAL_ACCOUNT); user.Practice.AccountContract = contract; if (practiceToReuse == null) this.db.AccountContracts.AddObject(contract); } else { // Creating a new account contract, getting info from the subscription string. var dicData = new Dictionary<string, dynamic>(StringComparer.InvariantCultureIgnoreCase) { { "1M", new { Price = Bus.Pro.PRICE_MONTH, PeriodSize = 1 } }, { "3M", new { Price = Bus.Pro.PRICE_QUARTER, PeriodSize = 3 } }, { "6M", new { Price = Bus.Pro.PRICE_SEMESTER, PeriodSize = 6 } }, { "12M", new { Price = Bus.Pro.PRICE_YEAR, PeriodSize = 12 } } }; var data = dicData[subscription]; var contract = user.Practice.AccountContract ?? new AccountContract(); contract.Practice = user.Practice; contract.ContractTypeId = (int)ContractTypes.ProfessionalContract; contract.IsTrial = false; contract.IssuanceDate = utcNow; contract.StartDate = null; // inderterminated (will be defined when user pays) contract.EndDate = null; // indeterminated contract.CustomText = null; contract.DoctorsLimit = null; // unknown at this moment (will be defined after user fills payment info) contract.PatientsLimit = null; // fixed limit for trial account // billings data can be redefined when the user fills payment info // for now these are the default values contract.IsPartialBillingInfo = true; // indicates that the billing info for this contract must be defined by the user contract.BillingAmount = Bus.Pro.PRICE_MONTH * (int)data.PeriodSize; contract.BillingDueDay = null; // payment method has no default (will be defined in the payment-info step) contract.BillingPaymentMethod = null; // payment method has no default (will be defined in the payment-info step) contract.BillingPeriodCount = null; contract.BillingPeriodSize = data.PeriodSize; contract.BillingPeriodType = "M"; contract.BillingDiscountAmount = (Bus.Pro.PRICE_MONTH * (int)data.PeriodSize) - data.Price; user.Practice.AccountExpiryDate = utcNow + Constants.MaxTimeToVerifyProfessionalAccount; user.Practice.AccountContract = contract; if (practiceToReuse == null) this.db.AccountContracts.AddObject(contract); } this.db.SaveChanges(); // if the new user is a doctor, create some other useful things // like some medical-certificates and a default health-insurance if (isNewDoctor) BusHelper.FillNewDoctorUtilityBelt(user.Doctor); if (practiceToReuse == null) { // adding message to the user so that he/she completes his/her profile informations // todo: add complete profile notification // If practice is being reused then notification was already sent. var notificationData = new CompletePracticeInfoNotificationData(); var notificationDataString = new JavaScriptSerializer().Serialize(notificationData); var dbNotification = new Notification { CreatedOn = this.GetUtcNow(), PracticeId = user.PracticeId, Data = notificationDataString, UserToId = user.Id, Type = NotificationConstants.COMPLETE_INFO_NOTIFICATION_TYPE, }; this.db.Notifications.AddObject(dbNotification); NotificationsHub.BroadcastDbNotification(dbNotification, notificationData); } if (practiceToReuse == null) { // If practice is being reused then these values were already set. user.Practice.Owner = user; user.Person.PracticeId = user.PracticeId; user.Administrator.PracticeId = user.PracticeId; if (user.Doctor != null) user.Doctor.PracticeId = user.PracticeId; } this.db.SaveChanges(); // Sending the confirmation e-mail to the new user. // This must be synchronous. // If practice is being reused then an email was already sent. if (practiceToReuse == null && emailMessageToUser != null) this.TrySendEmail(emailMessageToUser); // Log the user in. var loginModel = new LoginViewModel { Password = registrationData.Password, PracticeIdentifier = user.Practice.UrlIdentifier, RememberMe = false, UserNameOrEmail = registrationData.UserName, }; if (!SecurityManager.Login(this.HttpContext.Response.Cookies, loginModel, this.db.Users, out user, this.GetUtcNow())) { throw new Exception("Login cannot fail."); } if (subscription == "trial") return this.RedirectToAction("CreateAccountCompleted", new { practice = user.Practice.UrlIdentifier }); else return this.RedirectToAction("SetAccountPaymentInfo", new { practice = user.Practice.UrlIdentifier }); } } } this.ViewBag.MedicalSpecialtyOptions = this.db.SYS_MedicalSpecialty .ToList() .Select(me => new SelectListItem { Value = me.Id.ToString(), Text = me.Name }) .ToList(); this.ViewBag.MedicalEntityOptions = this.db.SYS_MedicalEntity .ToList() .Select(me => new SelectListItem { Value = me.Id.ToString(), Text = me.Name }) .ToList(); return this.View(registrationData); }
public ActionResult Edit(UserViewModel formModel) { var isEditingOrCreating = formModel.Id != null ? 'E' : 'C'; this.ViewBag.IsEditingOrCreating = isEditingOrCreating; var utcNow = this.GetUtcNow(); User user; // Normalizing the name of the person. if (!string.IsNullOrEmpty(formModel.FullName)) formModel.FullName = Regex.Replace(formModel.FullName, @"\s+", " ").Trim(); if (isEditingOrCreating == 'E') { // Note: User name cannot be edited, and should not be validated. this.ModelState.ClearPropertyErrors(() => formModel.UserName); user = this.db.Users.First(p => p.Id == formModel.Id); // TODO: suggest that r# use the attribute EdmScalarPropertyAttribute(IsNullable=false) // as a way to determine if a property can ever receive a null value or not // there was a bug in the line inside the following if, that could be detected by r# if it did consider that attribute. if (!string.IsNullOrWhiteSpace(formModel.FullName)) user.Person.FullName = formModel.FullName; user.Person.Gender = (short)formModel.Gender; // If there are model errors, we must return original user name to the view. formModel.UserName = user.UserName; } else { // UserName must not be null nor empty. if (string.IsNullOrWhiteSpace(formModel.UserName)) { this.ModelState.AddModelError(() => formModel.UserName, "Nome de usuário inválido."); } var loggedUser = this.DbUser; // Checking doctors limit of this account. if (formModel.IsDoctor) { var doctorsCount = this.DbPractice.Users.Count(u => u.DoctorId != null); if (doctorsCount >= this.DbPractice.AccountContract.DoctorsLimit) this.ModelState.AddModelError( "DoctorsLimit", "Essa conta está configurada para suportar até {0} médicos.", this.DbPractice.AccountContract.DoctorsLimit); } // Looking for another user with the same UserName or Email. var conflictingData = this.db.Users .Where(u => u.PracticeId == loggedUser.PracticeId) .Where(u => u.UserName == formModel.UserName || u.Person.Email == formModel.Email) .Select(u => new { u.UserName, u.Person.Email }) .ToList(); // Verifying wich fields are conflicting: Email. #warning [Validate] Must validate all emails. bool emailConflict = conflictingData.Any(c => c.Email == formModel.Email); // For every new user we must create a login, with a common // password used the first time the person logs in. // The only action allowed with this password, // is to change the password. var userData = new CreateAccountViewModel { UserName = formModel.UserName, Password = Constants.DEFAULT_PASSWORD, ConfirmPassword = Constants.DEFAULT_PASSWORD, EMail = formModel.Email, FullName = formModel.FullName, Gender = (short)formModel.Gender, }; // Creating the new user. // The user belongs to the same practice as the logged user. var result = SecurityManager.CreateUser(out user, userData, this.db.Users, utcNow, loggedUser.PracticeId); if (result == CreateUserResult.UserNameAlreadyInUse) { this.ModelState.AddModelError( () => formModel.UserName, // Todo: this message is also used in the AuthenticationController. "O nome de usuário não pode ser registrado pois já está em uso. " + "Note que nomes de usuário diferenciados por acentos, " + "maiúsculas/minúsculas ou por '.', '-' ou '_' não são permitidos." + "(Não é possível cadastrar 'MiguelAngelo' e 'miguel.angelo' no mesmo consultório."); } } #warning Must validade all emails, cannot repeat emails in the same practice. if (!formModel.IsDoctor && !formModel.IsAdministrador && !formModel.IsSecretary) this.ModelState.AddModelError("", "Usuário tem que ter pelo menos uma função: médico, administrador ou secretária."); // If the user being edited is a doctor, then we must check the fields that are required for medics. if (!formModel.IsDoctor) { // Removing validation error of medic properties, because this user is not a medic. this.ModelState.ClearPropertyErrors(() => formModel.MedicCRM); this.ModelState.ClearPropertyErrors(() => formModel.MedicalEntityId); this.ModelState.ClearPropertyErrors(() => formModel.MedicalSpecialtyId); this.ModelState.ClearPropertyErrors(() => formModel.MedicalSpecialtyName); this.ModelState.ClearPropertyErrors(() => formModel.MedicalEntityJurisdiction); } if (user != null) { if (formModel.DateOfBirth.HasValue) user.Person.DateOfBirth = ConvertToUtcDateTime(this.DbPractice, formModel.DateOfBirth.Value); user.Person.BirthPlace = formModel.BirthPlace; user.Person.CPF = formModel.Cpf; user.Person.CreatedOn = this.GetUtcNow(); user.Person.MaritalStatus = formModel.MaritalStatus; user.Person.Profession = formModel.Profissao; user.Person.Email = formModel.Email; user.Person.EmailGravatarHash = GravatarHelper.GetGravatarHash(formModel.Email); // handle address if (!user.Person.Addresses.Any()) user.Person.Addresses.Add( new Address { PracticeId = this.DbUser.PracticeId, CEP = formModel.Address.CEP, City = formModel.Address.City, Complement = formModel.Address.Complement, Neighborhood = formModel.Address.Neighborhood, StateProvince = formModel.Address.StateProvince, Street = formModel.Address.Street, }); // when the user is a doctor, we need to fill the properties of the doctor if (formModel.IsDoctor) { // Only administrators can change the role of an user. if (this.DbUser.AdministratorId != null) { // if user is already a doctor, we just edit the properties // otherwise we create a new doctor instance if (user.Doctor == null) { user.Doctor = new Doctor { PracticeId = this.DbUser.PracticeId, }; BusHelper.FillNewDoctorUtilityBelt(user.Doctor); } } // Changing the doctor's informations. if (!string.IsNullOrWhiteSpace(formModel.MedicCRM)) user.Doctor.CRM = formModel.MedicCRM; if (formModel.MedicalEntityId != null) { var me = this.db.SYS_MedicalEntity.First(me1 => me1.Id == formModel.MedicalEntityId); user.Doctor.MedicalEntityCode = me.Code; user.Doctor.MedicalEntityName = me.Name; } if (formModel.MedicalSpecialtyId != null) { var ms = this.db.SYS_MedicalSpecialty.First(ms1 => ms1.Id == formModel.MedicalSpecialtyId); user.Doctor.MedicalSpecialtyCode = ms.Code; user.Doctor.MedicalSpecialtyName = ms.Name; } if (formModel.MedicalEntityJurisdiction != null) user.Doctor.MedicalEntityJurisdiction = ((TypeEstadoBrasileiro)formModel.MedicalEntityJurisdiction.Value).ToString(); // Creating an unique UrlIdentifier for this doctor. // This does not consider UrlIdentifier's used by other kinds of objects. string urlId = GetUniqueDoctorUrlId(this.db.Doctors, formModel.FullName, this.DbPractice.Id); if (urlId == null && !string.IsNullOrWhiteSpace(formModel.FullName)) { this.ModelState.AddModelError( () => formModel.FullName, // Todo: this message is also used in the AuthenticationController. string.Format("Quantidade máxima de homônimos excedida para esta conta: {0}.", this.DbPractice.UrlIdentifier)); } if (!string.IsNullOrWhiteSpace(urlId)) user.Doctor.UrlIdentifier = urlId; } else { // Only administrators can change the role of an user. if (this.DbUser.AdministratorId != null) { if (user.Doctor != null) this.db.Doctors.DeleteObject(user.Doctor); // if the user is not a doctor, then we make sure // by assigning the doctor property to null user.Doctor = null; } } // when the user is an administrator if (formModel.IsAdministrador) { // Only administrators can change the role of an user. if (this.DbUser.AdministratorId != null) if (user.Administrator == null) user.Administrator = new Administrator { PracticeId = this.DbUser.PracticeId, }; } else { // Only administrators can change the role of an user. if (this.DbUser.AdministratorId != null) { if (user.Administrator != null) this.db.Administrators.DeleteObject(user.Administrator); user.Administrator = null; } } if (user.IsOwner) { if (!formModel.IsAdministrador) this.ModelState.AddModelError( () => formModel.IsAdministrador, "Não é possível remover o papel de administrador do proprietário da conta."); } // when the user is a secretary if (formModel.IsSecretary) { // Only administrators can change the role of an user. if (this.DbUser.AdministratorId != null) if (user.Secretary == null) user.Secretary = new Secretary { PracticeId = this.DbUser.PracticeId, }; } else { // Only administrators can change the role of an user. if (this.DbUser.AdministratorId != null) { if (user.Secretary != null) this.db.Secretaries.DeleteObject(user.Secretary); user.Secretary = null; } } } // If ModelState is still valid, save the objects to the database. if (this.ModelState.IsValid) { if (formModel.Id == null) { var notificationData = new NewUserCreatedNotificationData() { UserName = user.UserName }; var dbNotification = new Notification() { CreatedOn = this.GetUtcNow(), PracticeId = this.DbPractice.Id, UserToId = this.DbUser.Id, Type = NotificationConstants.NEW_USER_NOTIFICATION_TYPE, Data = new JavaScriptSerializer().Serialize(notificationData) }; this.db.Notifications.AddObject(dbNotification); this.db.SaveChanges(); NotificationsHub.BroadcastDbNotification(dbNotification, notificationData); } // Saving all the changes. this.db.SaveChanges(); return this.RedirectToAction("Details", new { id = user.Id }); } this.ViewBag.MedicalSpecialtyOptions = this.db.SYS_MedicalSpecialty .ToList() .Select(ms => new SelectListItem { Value = ms.Id.ToString(), Text = ms.Name }) .ToList(); this.ViewBag.MedicalEntityOptions = this.db.SYS_MedicalEntity .ToList() .Select(me => new SelectListItem { Value = me.Id.ToString(), Text = me.Name }) .ToList(); if (this.DbUser.AdministratorId != null || this.DbUser.IsOwner) this.ViewBag.CanEditRole = true; // Removes all duplicated messages. this.ModelState.RemoveDuplicates(); return this.View("Edit", formModel); }