/// <summary> /// Invites a user /// </summary> /// <param name="userSave"></param> /// <returns></returns> /// <remarks> /// This will email the user an invite and generate a token that will be validated in the email /// </remarks> public async Task <UserDisplay> PostInviteUser(UserInvite userSave) { if (userSave == null) { throw new ArgumentNullException("userSave"); } if (userSave.Message.IsNullOrWhiteSpace()) { ModelState.AddModelError("Message", "Message cannot be empty"); } if (ModelState.IsValid == false) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } IUser user; if (Current.Configs.Settings().Security.UsernameIsEmail) { //ensure it's the same userSave.Username = userSave.Email; } else { //first validate the username if we're showing it user = CheckUniqueUsername(userSave.Username, u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue); } user = CheckUniqueEmail(userSave.Email, u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue); var userMgr = TryGetOwinContext().Result.GetBackOfficeUserManager(); if (!EmailSender.CanSendRequiredEmail && !userMgr.HasSendingUserInviteEventHandler) { throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse("No Email server is configured")); } //Perform authorization here to see if the current user can actually save this user with the info being requested var authHelper = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService); var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, user, null, null, userSave.UserGroups); if (canSaveUser == false) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result)); } if (user == null) { //we want to create the user with the UserManager, this ensures the 'empty' (special) password //format is applied without us having to duplicate that logic var identityUser = BackOfficeIdentityUser.CreateNew(userSave.Username, userSave.Email, GlobalSettings.DefaultUILanguage); identityUser.Name = userSave.Name; var created = await UserManager.CreateAsync(identityUser); if (created.Succeeded == false) { throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse(string.Join(", ", created.Errors))); } //now re-look the user back up user = Services.UserService.GetByEmail(userSave.Email); } //map the save info over onto the user user = Mapper.Map(userSave, user); //ensure the invited date is set user.InvitedDate = DateTime.Now; //Save the updated user (which will process the user groups too) Services.UserService.Save(user); var display = Mapper.Map <UserDisplay>(user); var inviteArgs = new UserInviteEventArgs( Request.TryGetHttpContext().Result.GetCurrentRequestIpAddress(), performingUser: Security.GetUserId().Result, userSave, user); try { userMgr.RaiseSendingUserInvite(inviteArgs); } catch (Exception ex) { Logger.Error <UsersController>(ex, "An error occurred in a custom event handler while inviting the user"); throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse($"An error occurred inviting the user (check logs for more info): {ex.Message}")); } // If the event is handled then no need to send the email if (inviteArgs.InviteHandled) { // if no user result was created then map the minimum args manually for the UI if (!inviteArgs.ShowUserResult) { display = new UserDisplay { Name = userSave.Name, Email = userSave.Email, Username = userSave.Username }; } } else { //send the email await SendUserInviteEmailAsync(display, Security.CurrentUser.Name, Security.CurrentUser.Email, user, userSave.Message); } display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/resendInviteHeader"), Services.TextService.Localize("speechBubbles/resendInviteSuccess", new[] { user.Name })); return(display); }
/// <summary> /// Creates a new user /// </summary> /// <param name="userSave"></param> /// <returns></returns> public async Task <UserDisplay> PostCreateUser(UserInvite userSave) { if (userSave == null) { throw new ArgumentNullException("userSave"); } if (ModelState.IsValid == false) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } if (Current.Configs.Settings().Security.UsernameIsEmail) { //ensure they are the same if we're using it userSave.Username = userSave.Email; } else { //first validate the username if were showing it CheckUniqueUsername(userSave.Username, null); } CheckUniqueEmail(userSave.Email, null); //Perform authorization here to see if the current user can actually save this user with the info being requested var authHelper = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService); var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, null, null, null, userSave.UserGroups); if (canSaveUser == false) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result)); } //we want to create the user with the UserManager, this ensures the 'empty' (special) password //format is applied without us having to duplicate that logic var identityUser = BackOfficeIdentityUser.CreateNew(userSave.Username, userSave.Email, GlobalSettings.DefaultUILanguage); identityUser.Name = userSave.Name; var created = await UserManager.CreateAsync(identityUser); if (created.Succeeded == false) { throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse(string.Join(", ", created.Errors))); } //we need to generate a password, however we can only do that if the user manager has a password validator that //we can read values from var passwordValidator = UserManager.PasswordValidator as PasswordValidator; var resetPassword = string.Empty; if (passwordValidator != null) { var password = UserManager.GeneratePassword(); var result = await UserManager.AddPasswordAsync(identityUser.Id, password); if (result.Succeeded == false) { throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse(string.Join(", ", created.Errors))); } resetPassword = password; } //now re-look the user back up which will now exist var user = Services.UserService.GetByEmail(userSave.Email); //map the save info over onto the user user = Mapper.Map(userSave, user); //since the back office user is creating this user, they will be set to approved user.IsApproved = true; Services.UserService.Save(user); var display = Mapper.Map <UserDisplay>(user); display.ResetPasswordValue = resetPassword; return(display); }
public async Task <UserDisplay> PostSaveUser(UserSave userSave) { if (userSave == null) { throw new ArgumentNullException("userSave"); } if (ModelState.IsValid == false) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } var intId = userSave.Id.TryConvertTo <int>(); if (intId.Success == false) { throw new HttpResponseException(HttpStatusCode.NotFound); } var found = Services.UserService.GetUserById(intId.Result); if (found == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } //Perform authorization here to see if the current user can actually save this user with the info being requested var authHelper = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService); var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, found, userSave.StartContentIds, userSave.StartMediaIds, userSave.UserGroups); if (canSaveUser == false) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result)); } var hasErrors = false; var existing = Services.UserService.GetByEmail(userSave.Email); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Email", "A user with the email already exists"); hasErrors = true; } existing = Services.UserService.GetByUsername(userSave.Username); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Username", "A user with the username already exists"); hasErrors = true; } // going forward we prefer to align usernames with email, so we should cross-check to make sure // the email or username isn't somehow being used by anyone. existing = Services.UserService.GetByEmail(userSave.Username); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Username", "A user using this as their email already exists"); hasErrors = true; } existing = Services.UserService.GetByUsername(userSave.Email); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Email", "A user using this as their username already exists"); hasErrors = true; } // if the found user has their email for username, we want to keep this synced when changing the email. // we have already cross-checked above that the email isn't colliding with anything, so we can safely assign it here. if (Current.Configs.Settings().Security.UsernameIsEmail&& found.Username == found.Email && userSave.Username != userSave.Email) { userSave.Username = userSave.Email; } if (hasErrors) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } //merge the save data onto the user var user = Mapper.Map(userSave, found); Services.UserService.Save(user); var display = Mapper.Map <UserDisplay>(user); display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/operationSavedHeader"), Services.TextService.Localize("speechBubbles/editUserSaved")); return(display); }
/// <summary> /// Invites a user /// </summary> /// <param name="userSave"></param> /// <returns></returns> /// <remarks> /// This will email the user an invite and generate a token that will be validated in the email /// </remarks> public async Task <UserDisplay> PostInviteUser(UserInvite userSave) { if (userSave == null) { throw new ArgumentNullException("userSave"); } if (userSave.Message.IsNullOrWhiteSpace()) { ModelState.AddModelError("Message", "Message cannot be empty"); } if (ModelState.IsValid == false) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } if (EmailSender.CanSendRequiredEmail == false) { throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse("No Email server is configured")); } IUser user; if (Current.Configs.Settings().Security.UsernameIsEmail) { //ensure it's the same userSave.Username = userSave.Email; } else { //first validate the username if we're showing it user = CheckUniqueUsername(userSave.Username, u => u.LastLoginDate != default(DateTime) || u.EmailConfirmedDate.HasValue); } user = CheckUniqueEmail(userSave.Email, u => u.LastLoginDate != default(DateTime) || u.EmailConfirmedDate.HasValue); //Perform authorization here to see if the current user can actually save this user with the info being requested var authHelper = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService); var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, user, null, null, userSave.UserGroups); if (canSaveUser == false) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result)); } if (user == null) { //we want to create the user with the UserManager, this ensures the 'empty' (special) password //format is applied without us having to duplicate that logic var identityUser = BackOfficeIdentityUser.CreateNew(userSave.Username, userSave.Email, GlobalSettings.DefaultUILanguage); identityUser.Name = userSave.Name; var created = await UserManager.CreateAsync(identityUser); if (created.Succeeded == false) { throw new HttpResponseException( Request.CreateNotificationValidationErrorResponse(string.Join(", ", created.Errors))); } //now re-look the user back up user = Services.UserService.GetByEmail(userSave.Email); } //map the save info over onto the user user = Mapper.Map(userSave, user); //ensure the invited date is set user.InvitedDate = DateTime.Now; //Save the updated user Services.UserService.Save(user); var display = Mapper.Map <UserDisplay>(user); //send the email await SendUserInviteEmailAsync(display, Security.CurrentUser.Name, Security.CurrentUser.Email, user, userSave.Message); display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/resendInviteHeader"), Services.TextService.Localize("speechBubbles/resendInviteSuccess", new[] { user.Name })); return(display); }
public UserDisplay PostSaveUser(UserSave userSave) { if (userSave == null) { throw new ArgumentNullException(nameof(userSave)); } if (ModelState.IsValid == false) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } var intId = userSave.Id.TryConvertTo <int>(); if (intId.Success == false) { throw new HttpResponseException(HttpStatusCode.NotFound); } var found = Services.UserService.GetUserById(intId.Result); if (found == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } //Perform authorization here to see if the current user can actually save this user with the info being requested var authHelper = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService); var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, found, userSave.StartContentIds, userSave.StartMediaIds, userSave.UserGroups); if (canSaveUser == false) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result)); } var hasErrors = false; // we need to check if there's any Deny Local login providers present, if so we need to ensure that the user's email address cannot be changed var owinContext = Request.TryGetOwinContext().Result; var hasDenyLocalLogin = owinContext.Authentication.HasDenyLocalLogin(); if (hasDenyLocalLogin) { userSave.Email = found.Email; // it cannot change, this would only happen if people are mucking around with the request } var existing = Services.UserService.GetByEmail(userSave.Email); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Email", "A user with the email already exists"); hasErrors = true; } existing = Services.UserService.GetByUsername(userSave.Username); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Username", "A user with the username already exists"); hasErrors = true; } // going forward we prefer to align usernames with email, so we should cross-check to make sure // the email or username isn't somehow being used by anyone. existing = Services.UserService.GetByEmail(userSave.Username); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Username", "A user using this as their email already exists"); hasErrors = true; } existing = Services.UserService.GetByUsername(userSave.Email); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Email", "A user using this as their username already exists"); hasErrors = true; } // if the found user has their email for username, we want to keep this synced when changing the email. // we have already cross-checked above that the email isn't colliding with anything, so we can safely assign it here. if (Current.Configs.Settings().Security.UsernameIsEmail&& found.Username == found.Email && userSave.Username != userSave.Email) { userSave.Username = userSave.Email; } if (hasErrors) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } //merge the save data onto the user var user = Mapper.Map(userSave, found); Services.UserService.Save(user); var display = Mapper.Map <UserDisplay>(user); // determine if the user has changed their own language; var userHasChangedOwnLanguage = user.Id == Security.CurrentUser.Id && Security.CurrentUser.Language != user.Language; var textToLocalise = userHasChangedOwnLanguage ? "speechBubbles/operationSavedHeaderReloadUser" : "speechBubbles/operationSavedHeader"; var culture = userHasChangedOwnLanguage ? CultureInfo.GetCultureInfo(user.Language) : Thread.CurrentThread.CurrentUICulture; display.AddSuccessNotification(Services.TextService.Localize(textToLocalise, culture), Services.TextService.Localize("speechBubbles/editUserSaved", culture)); return(display); }
/// <summary> /// Saves a user /// </summary> /// <param name="userSave"></param> /// <returns></returns> public async Task <UserDisplay> PostSaveUser(UserSave userSave) { if (userSave == null) { throw new ArgumentNullException("userSave"); } if (ModelState.IsValid == false) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } var intId = userSave.Id.TryConvertTo <int>(); if (intId.Success == false) { throw new HttpResponseException(HttpStatusCode.NotFound); } var found = Services.UserService.GetUserById(intId.Result); if (found == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } //Perform authorization here to see if the current user can actually save this user with the info being requested var authHelper = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService); var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, found, userSave.StartContentIds, userSave.StartMediaIds, userSave.UserGroups); if (canSaveUser == false) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result)); } var hasErrors = false; var existing = Services.UserService.GetByEmail(userSave.Email); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Email", "A user with the email already exists"); hasErrors = true; } existing = Services.UserService.GetByUsername(userSave.Username); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Username", "A user with the username already exists"); hasErrors = true; } // going forward we prefer to align usernames with email, so we should cross-check to make sure // the email or username isn't somehow being used by anyone. existing = Services.UserService.GetByEmail(userSave.Username); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Username", "A user using this as their email already exists"); hasErrors = true; } existing = Services.UserService.GetByUsername(userSave.Email); if (existing != null && existing.Id != userSave.Id) { ModelState.AddModelError("Email", "A user using this as their username already exists"); hasErrors = true; } // if the found user has his email for username, we want to keep this synced when changing the email. // we have already cross-checked above that the email isn't colliding with anything, so we can safely assign it here. if (found.Username == found.Email && userSave.Username != userSave.Email) { userSave.Username = userSave.Email; } if (userSave.ChangePassword != null) { var passwordChanger = new PasswordChanger(Logger, Services.UserService, UmbracoContext.HttpContext); var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, found, userSave.ChangePassword, UserManager); if (passwordChangeResult.Success) { var userMgr = this.TryGetOwinContext().Result.GetBackOfficeUserManager(); //raise the event - NOTE that the ChangePassword.Reset value here doesn't mean it's been 'reset', it means //it's been changed by a back office user if (userSave.ChangePassword.Reset.HasValue && userSave.ChangePassword.Reset.Value) { userMgr.RaisePasswordChangedEvent(intId.Result); } //need to re-get the user found = Services.UserService.GetUserById(intId.Result); } else { hasErrors = true; foreach (var memberName in passwordChangeResult.Result.ChangeError.MemberNames) { ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage); } } } if (hasErrors) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } //merge the save data onto the user var user = Mapper.Map(userSave, found); Services.UserService.Save(user); var display = Mapper.Map <UserDisplay>(user); display.AddSuccessNotification(Services.TextService.Localize("speechBubbles/operationSavedHeader"), Services.TextService.Localize("speechBubbles/editUserSaved")); return(display); }