Beispiel #1
0
    public ActionResult<UserDisplay?> PostSaveUser(UserSave userSave)
    {
        if (userSave == null)
        {
            throw new ArgumentNullException(nameof(userSave));
        }

        if (ModelState.IsValid == false)
        {
            return ValidationProblem(ModelState);
        }

        IUser? found = _userService.GetUserById(userSave.Id);
        if (found == null)
        {
            return NotFound();
        }

        //Perform authorization here to see if the current user can actually save this user with the info being requested
        Attempt<string?> canSaveUser = _userEditorAuthorizationHelper.IsAuthorized(
            _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser, found, userSave.StartContentIds,
            userSave.StartMediaIds, userSave.UserGroups);
        if (canSaveUser == false)
        {
            return 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 hasDenyLocalLogin = _externalLogins.HasDenyLocalLogin();
        if (hasDenyLocalLogin)
        {
            userSave.Email =
                found.Email; // it cannot change, this would only happen if people are mucking around with the request
        }

        IUser? existing = _userService.GetByEmail(userSave.Email);
        if (existing != null && existing.Id != userSave.Id)
        {
            ModelState.AddModelError("Email", "A user with the email already exists");
            hasErrors = true;
        }

        existing = _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 = _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 = _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 (_securitySettings.UsernameIsEmail && found.Username == found.Email && userSave.Username != userSave.Email)
        {
            userSave.Username = userSave.Email;
        }

        if (hasErrors)
        {
            return ValidationProblem(ModelState);
        }

        //merge the save data onto the user
        IUser user = _umbracoMapper.Map(userSave, found);

        _userService.Save(user);

        UserDisplay? display = _umbracoMapper.Map<UserDisplay>(user);

        // determine if the user has changed their own language;
        IUser? currentUser = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;
        var userHasChangedOwnLanguage =
            user.Id == currentUser?.Id && currentUser.Language != user.Language;

        var textToLocalise = userHasChangedOwnLanguage ? "operationSavedHeaderReloadUser" : "operationSavedHeader";
        CultureInfo culture = userHasChangedOwnLanguage
            ? CultureInfo.GetCultureInfo(user.Language!)
            : Thread.CurrentThread.CurrentUICulture;
        display?.AddSuccessNotification(_localizedTextService.Localize("speechBubbles", textToLocalise, culture),
            _localizedTextService.Localize("speechBubbles", "editUserSaved", culture));
        return display;
    }
Beispiel #2
0
    /// <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<ActionResult<UserDisplay?>> PostInviteUser(UserInvite userSave)
    {
        if (userSave == null)
        {
            throw new ArgumentNullException(nameof(userSave));
        }

        if (userSave.Message.IsNullOrWhiteSpace())
        {
            ModelState.AddModelError("Message", "Message cannot be empty");
        }

        if (_securitySettings.UsernameIsEmail)
        {
            // ensure it's the same
            userSave.Username = userSave.Email;
        }
        else
        {
            // first validate the username if we're showing it
            ActionResult<IUser?> userResult = CheckUniqueUsername(userSave.Username,
                u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue);
            if (userResult.Result is not null)
            {
                return userResult.Result;
            }
        }

        IUser? user = CheckUniqueEmail(userSave.Email,
            u => u.LastLoginDate != default || u.EmailConfirmedDate.HasValue);

        if (ModelState.IsValid == false)
        {
            return ValidationProblem(ModelState);
        }

        if (!_emailSender.CanSendRequiredEmail())
        {
            return ValidationProblem("No Email server is configured");
        }

        // Perform authorization here to see if the current user can actually save this user with the info being requested
        Attempt<string?> canSaveUser = _userEditorAuthorizationHelper.IsAuthorized(
            _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser, user, null, null, userSave.UserGroups);
        if (canSaveUser == false)
        {
            return ValidationProblem(canSaveUser.Result, StatusCodes.Status401Unauthorized);
        }

        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(_globalSettings, userSave.Username, userSave.Email,
                _globalSettings.DefaultUILanguage);
            identityUser.Name = userSave.Name;

            IdentityResult created = await _userManager.CreateAsync(identityUser);
            if (created.Succeeded == false)
            {
                return ValidationProblem(created.Errors.ToErrorMessage());
            }

            // now re-look the user back up
            user = _userService.GetByEmail(userSave.Email);
        }

        // map the save info over onto the user
        user = _umbracoMapper.Map(userSave, user);

        if (user is not null)
        {
            // ensure the invited date is set
            user.InvitedDate = DateTime.Now;

            // Save the updated user (which will process the user groups too)
            _userService.Save(user);
        }

        UserDisplay? display = _umbracoMapper.Map<UserDisplay>(user);

        // send the email
        await SendUserInviteEmailAsync(display, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Name,
            _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Email, user, userSave.Message);

        display?.AddSuccessNotification(_localizedTextService.Localize("speechBubbles", "resendInviteHeader"),
            _localizedTextService.Localize("speechBubbles", "resendInviteSuccess", new[] { user?.Name }));
        return display;
    }