using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.BackOffice; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.Models; using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Media; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Extensions; using Umbraco.Web.BackOffice.Filters; using Umbraco.Web.BackOffice.Security; using Umbraco.Web.Common.Attributes; using Umbraco.Web.Common.Exceptions; using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Security; namespace Umbraco.Web.BackOffice.Controllers { /// /// Controller to back the User.Resource service, used for fetching user data when already authenticated. user.service is currently used for handling authentication /// [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] public class CurrentUserController : UmbracoAuthorizedJsonController { private readonly IMediaFileSystem _mediaFileSystem; private readonly ContentSettings _contentSettings; private readonly IHostingEnvironment _hostingEnvironment; private readonly IImageUrlGenerator _imageUrlGenerator; private readonly IWebSecurity _webSecurity; private readonly IUserService _userService; private readonly UmbracoMapper _umbracoMapper; private readonly BackOfficeUserManager _backOfficeUserManager; private readonly ILogger _logger; private readonly ILocalizedTextService _localizedTextService; private readonly AppCaches _appCaches; private readonly IShortStringHelper _shortStringHelper; public CurrentUserController( IMediaFileSystem mediaFileSystem, IOptions contentSettings, IHostingEnvironment hostingEnvironment, IImageUrlGenerator imageUrlGenerator, IWebSecurity webSecurity, IUserService userService, UmbracoMapper umbracoMapper, BackOfficeUserManager backOfficeUserManager, ILogger logger, ILocalizedTextService localizedTextService, AppCaches appCaches, IShortStringHelper shortStringHelper) { _mediaFileSystem = mediaFileSystem; _contentSettings = contentSettings.Value; _hostingEnvironment = hostingEnvironment; _imageUrlGenerator = imageUrlGenerator; _webSecurity = webSecurity; _userService = userService; _umbracoMapper = umbracoMapper; _backOfficeUserManager = backOfficeUserManager; _logger = logger; _localizedTextService = localizedTextService; _appCaches = appCaches; _shortStringHelper = shortStringHelper; } /// /// Returns permissions for all nodes passed in for the current user /// /// /// [HttpPost] public Dictionary GetPermissions(int[] nodeIds) { var permissions = _userService .GetPermissions(_webSecurity.CurrentUser, nodeIds); var permissionsDictionary = new Dictionary(); foreach (var nodeId in nodeIds) { var aggregatePerms = permissions.GetAllPermissions(nodeId).ToArray(); permissionsDictionary.Add(nodeId, aggregatePerms); } return permissionsDictionary; } /// /// Checks a nodes permission for the current user /// /// /// /// [HttpGet] public bool HasPermission(string permissionToCheck, int nodeId) { var p = _userService.GetPermissions(_webSecurity.CurrentUser, nodeId).GetAllPermissions(); if (p.Contains(permissionToCheck.ToString(CultureInfo.InvariantCulture))) { return true; } return false; } /// /// Saves a tour status for the current user /// /// /// public IEnumerable PostSetUserTour(UserTourStatus status) { if (status == null) throw new ArgumentNullException(nameof(status)); List userTours; if (_webSecurity.CurrentUser.TourData.IsNullOrWhiteSpace()) { userTours = new List { status }; _webSecurity.CurrentUser.TourData = JsonConvert.SerializeObject(userTours); _userService.Save(_webSecurity.CurrentUser); return userTours; } userTours = JsonConvert.DeserializeObject>(_webSecurity.CurrentUser.TourData).ToList(); var found = userTours.FirstOrDefault(x => x.Alias == status.Alias); if (found != null) { //remove it and we'll replace it next userTours.Remove(found); } userTours.Add(status); _webSecurity.CurrentUser.TourData = JsonConvert.SerializeObject(userTours); _userService.Save(_webSecurity.CurrentUser); return userTours; } /// /// Returns the user's tours /// /// public IEnumerable GetUserTours() { if (_webSecurity.CurrentUser.TourData.IsNullOrWhiteSpace()) return Enumerable.Empty(); var userTours = JsonConvert.DeserializeObject>(_webSecurity.CurrentUser.TourData); return userTours; } /// /// When a user is invited and they click on the invitation link, they will be partially logged in /// where they can set their username/password /// /// /// /// /// This only works when the user is logged in (partially) /// [UmbracoAuthorize(redirectToUmbracoLogin: false, requireApproval : true)] public async Task PostSetInvitedUserPassword([FromBody]string newPassword) { var user = await _backOfficeUserManager.FindByIdAsync(_webSecurity.GetUserId().ResultOr(0).ToString()); if (user == null) throw new InvalidOperationException("Could not find user"); var result = await _backOfficeUserManager.AddPasswordAsync(user, newPassword); if (result.Succeeded == false) { //it wasn't successful, so add the change error to the model state, we've name the property alias _umb_password on the form // so that is why it is being used here. ModelState.AddModelError("value", result.Errors.ToErrorMessage()); throw HttpResponseException.CreateValidationErrorResponse(ModelState); } //They've successfully set their password, we can now update their user account to be approved _webSecurity.CurrentUser.IsApproved = true; //They've successfully set their password, and will now get fully logged into the back office, so the lastlogindate is set so the backoffice shows they have logged in _webSecurity.CurrentUser.LastLoginDate = DateTime.UtcNow; _userService.Save(_webSecurity.CurrentUser); //now we can return their full object since they are now really logged into the back office var userDisplay = _umbracoMapper.Map(_webSecurity.CurrentUser); userDisplay.SecondsUntilTimeout = HttpContext.User.GetRemainingAuthSeconds(); return userDisplay; } [AppendUserModifiedHeader] public async Task PostSetAvatar(IList files) { //borrow the logic from the user controller return await UsersController.PostSetAvatarInternal(files, _userService, _appCaches.RuntimeCache, _mediaFileSystem, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, _webSecurity.GetUserId().ResultOr(0)); } /// /// Changes the users password /// /// /// /// If the password is being reset it will return the newly reset password, otherwise will return an empty value /// public async Task> PostChangePassword(ChangingPasswordModel data) { var passwordChanger = new PasswordChanger(_logger); var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(_webSecurity.CurrentUser, _webSecurity.CurrentUser, data, _backOfficeUserManager); if (passwordChangeResult.Success) { //even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword var result = new ModelWithNotifications(passwordChangeResult.Result.ResetPassword); result.AddSuccessNotification(_localizedTextService.Localize("user/password"), _localizedTextService.Localize("user/passwordChanged")); return result; } foreach (var memberName in passwordChangeResult.Result.ChangeError.MemberNames) { ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage); } throw HttpResponseException.CreateValidationErrorResponse(ModelState); } [UmbracoAuthorize] [ValidateAngularAntiForgeryToken] public async Task> GetCurrentUserLinkedLogins() { var identityUser = await _backOfficeUserManager.FindByIdAsync(_webSecurity.GetUserId().ResultOr(0).ToString()); return identityUser.Logins.ToDictionary(x => x.LoginProvider, x => x.ProviderKey); } } }