Fixes U4-10111 Changing email on a user doesn't show the username field
This commit is contained in:
@@ -9,7 +9,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
|
||||
public const string AdminGroupAlias = "admin";
|
||||
|
||||
|
||||
public const string BackOfficeAuthenticationType = "UmbracoBackOffice";
|
||||
public const string BackOfficeExternalAuthenticationType = "UmbracoExternalCookie";
|
||||
public const string BackOfficeExternalCookieName = "UMB_EXTLOGIN";
|
||||
@@ -17,6 +17,7 @@ namespace Umbraco.Core
|
||||
public const string BackOfficeTwoFactorAuthenticationType = "UmbracoTwoFactorCookie";
|
||||
|
||||
internal const string EmptyPasswordPrefix = "___UIDEMPTYPWORD__";
|
||||
internal const string ForceReAuthFlag = "umbraco-force-auth";
|
||||
|
||||
/// <summary>
|
||||
/// The prefix used for external identity providers for their authentication type
|
||||
|
||||
@@ -106,6 +106,16 @@ namespace Umbraco.Core.Models.Identity
|
||||
|
||||
private ObservableCollection<IIdentityUserLogin> _logins;
|
||||
private Lazy<IEnumerable<IIdentityUserLogin>> _getLogins;
|
||||
private List<IdentityUserRole<string>> _roles;
|
||||
|
||||
//TODO: We need to override this but need to wait until the rest of the PRs are merged in
|
||||
///// <summary>
|
||||
///// Override Roles because the value of these are the user's group aliases
|
||||
///// </summary>
|
||||
//public override ICollection<IdentityUserRole<string>> Roles
|
||||
//{
|
||||
// get { return _roles ?? (_roles = Groups.Select(x => x.Alias).ToArray()); }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Used to set a lazy call back to populate the user's Login list
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace Umbraco.Core.Models.Identity
|
||||
.ConstructUsing((BackOfficeIdentityUser user) => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id'
|
||||
.ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id))
|
||||
.ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections))
|
||||
.ForMember(detail => detail.Roles, opt => opt.MapFrom(user => user.Groups))
|
||||
//TODO: This should really be mapping Roles -> Roles
|
||||
.ForMember(detail => detail.Roles, opt => opt.MapFrom(user => user.Groups.Select(x => x.Alias).ToArray()))
|
||||
.ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name))
|
||||
//When mapping to UserData which is used in the authcookie we want ALL start nodes including ones defined on the groups
|
||||
.ForMember(detail => detail.StartContentNodes, opt => opt.MapFrom(user => user.AllStartContentIds))
|
||||
|
||||
@@ -218,7 +218,7 @@ namespace Umbraco.Core.Security
|
||||
public static bool RenewUmbracoAuthTicket(this HttpContextBase http)
|
||||
{
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
http.Items["umbraco-force-auth"] = true;
|
||||
http.Items[Constants.Security.ForceReAuthFlag] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ namespace Umbraco.Core.Security
|
||||
internal static bool RenewUmbracoAuthTicket(this HttpContext http)
|
||||
{
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
http.Items["umbraco-force-auth"] = true;
|
||||
http.Items[Constants.Security.ForceReAuthFlag] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,11 @@ namespace Umbraco.Core.Security
|
||||
RealName = user.Name,
|
||||
AllowedApplications = user.AllowedSections,
|
||||
Culture = user.Culture,
|
||||
Roles = user.Roles.Select(x => x.RoleId).ToArray(),
|
||||
//TODO: In order for this to work, the user.Roles would need to be filled in!
|
||||
//Currently that is not the case because the BackOfficeIdentityUser deals with Groups (which we need to update)
|
||||
//For now, I'll fix this by using the user.Groups instead
|
||||
//Roles = user.Roles.Select(x => x.RoleId).ToArray(),
|
||||
Roles = user.Groups.Select(x => x.Alias).ToArray(),
|
||||
StartContentNodes = user.StartContentIds,
|
||||
StartMediaNodes = user.StartMediaIds,
|
||||
SessionId = user.SecurityStamp
|
||||
|
||||
@@ -32,7 +32,8 @@ namespace Umbraco.Core.Security
|
||||
var identity = Thread.CurrentPrincipal.GetUmbracoIdentity();
|
||||
if (identity != null)
|
||||
{
|
||||
var user = userService.GetByUsername(identity.Username);
|
||||
var user = userService.GetUserById(identity.Id.TryConvertTo<int>().Result);
|
||||
if (user == null) throw new InvalidOperationException("No user with username " + identity.Username + " found");
|
||||
var userIsAdmin = user.IsAdmin();
|
||||
if (userIsAdmin)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data.Common;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -214,14 +215,8 @@ namespace Umbraco.Core.Services
|
||||
Save(membershipUser);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method exists so that Umbraco developers can use one entry point to create/update users if they choose to.
|
||||
/// </remarks>
|
||||
/// <param name="user">The user to save the password for</param>
|
||||
/// <param name="password">The password to save</param>
|
||||
[Obsolete("ASP.NET Identity APIs like the BackOfficeUserManager should be used to manage passwords, this will not work with correct security practices because you would need the existing password")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void SavePassword(IUser user, string password)
|
||||
{
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
|
||||
@@ -149,7 +149,8 @@ namespace Umbraco.Web.Editors
|
||||
/// cookies which means that the auth cookie could be valid but the csrf cookies are no longer there, in that case we need to re-set the csrf cookies.
|
||||
/// </remarks>
|
||||
[WebApi.UmbracoAuthorize]
|
||||
[SetAngularAntiForgeryTokens]
|
||||
[SetAngularAntiForgeryTokens]
|
||||
[VerifyIfUserTicketDataIsStale]
|
||||
public UserDetail GetCurrentUser()
|
||||
{
|
||||
var user = UmbracoContext.Security.CurrentUser;
|
||||
@@ -194,7 +195,8 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//TODO: This should be on the CurrentUserController?
|
||||
[WebApi.UmbracoAuthorize]
|
||||
[ValidateAngularAntiForgeryToken]
|
||||
public async Task<Dictionary<string, string>> GetCurrentUserLinkedLogins()
|
||||
|
||||
@@ -325,7 +325,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id))
|
||||
.ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections))
|
||||
.ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name))
|
||||
.ForMember(detail => detail.Roles, opt => opt.MapFrom(user => user.Groups.ToArray()))
|
||||
.ForMember(detail => detail.Roles, opt => opt.MapFrom(user => user.Groups.Select(x => x.Alias).ToArray()))
|
||||
.ForMember(detail => detail.StartContentNodes, opt => opt.MapFrom(user => user.AllStartContentIds))
|
||||
.ForMember(detail => detail.StartMediaNodes, opt => opt.MapFrom(user => user.AllStartMediaIds))
|
||||
.ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.Username))
|
||||
|
||||
@@ -96,8 +96,8 @@ namespace Umbraco.Web.Security.Identity
|
||||
if (request.Uri.AbsolutePath.InvariantEquals(_getRemainingSecondsPath)) return false;
|
||||
|
||||
if (//check the explicit flag
|
||||
(checkForceAuthTokens && ctx.Get<bool?>("umbraco-force-auth") != null)
|
||||
|| (checkForceAuthTokens && httpCtx.Success && httpCtx.Result.Items["umbraco-force-auth"] != null)
|
||||
(checkForceAuthTokens && ctx.Get<bool?>(Constants.Security.ForceReAuthFlag) != null)
|
||||
|| (checkForceAuthTokens && httpCtx.Success && httpCtx.Result.Items[Constants.Security.ForceReAuthFlag] != null)
|
||||
//check back office
|
||||
|| request.Uri.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath)
|
||||
//check installer
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Umbraco.Web.Security.Identity
|
||||
|
||||
var httpCtx = Context.TryGetHttpContext();
|
||||
//check for the special flag in either the owin or http context
|
||||
var shouldRenew = Context.Get<bool?>("umbraco-force-auth") != null || (httpCtx.Success && httpCtx.Result.Items["umbraco-force-auth"] != null);
|
||||
var shouldRenew = Context.Get<bool?>(Constants.Security.ForceReAuthFlag) != null || (httpCtx.Success && httpCtx.Result.Items[Constants.Security.ForceReAuthFlag] != null);
|
||||
|
||||
if (shouldRenew)
|
||||
{
|
||||
|
||||
@@ -798,6 +798,7 @@
|
||||
<Compile Include="WebApi\Filters\UmbracoTreeAuthorizeAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\UmbracoUseHttps.cs" />
|
||||
<Compile Include="WebApi\Filters\ValidateAngularAntiForgeryTokenAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\VerifyIfUserTicketDataIsStaleAttribute.cs" />
|
||||
<Compile Include="WebApi\HttpControllerContextExtensions.cs" />
|
||||
<Compile Include="WebApi\CustomDateTimeConvertor.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentSortOrder.cs" />
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.Filters;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.Security;
|
||||
using UserExtensions = Umbraco.Core.Models.UserExtensions;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Filters
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This filter will check if the current Principal/Identity assigned to the request has stale data in it compared
|
||||
/// to what is persisted for the current user and will update the current auth ticket with the correct data if required.
|
||||
/// </summary>
|
||||
public sealed class VerifyIfUserTicketDataIsStaleAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
|
||||
{
|
||||
await CheckStaleData(actionContext);
|
||||
}
|
||||
|
||||
public override async Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
|
||||
{
|
||||
await CheckStaleData(actionExecutedContext.ActionContext);
|
||||
}
|
||||
|
||||
private async Task CheckStaleData(HttpActionContext actionContext)
|
||||
{
|
||||
var identity = actionContext.RequestContext.Principal.Identity as UmbracoBackOfficeIdentity;
|
||||
if (identity == null) return;
|
||||
|
||||
var userId = identity.Id.TryConvertTo<int>();
|
||||
if (userId == false) return;
|
||||
|
||||
var user = ApplicationContext.Current.Services.UserService.GetUserById(userId.Result);
|
||||
if (user == null) return;
|
||||
|
||||
if (user.Username != identity.Username)
|
||||
{
|
||||
await ReSync(user, identity, actionContext);
|
||||
return;
|
||||
}
|
||||
|
||||
var culture = UserExtensions.GetUserCulture(user, ApplicationContext.Current.Services.TextService).ToString();
|
||||
if (culture != identity.Culture)
|
||||
{
|
||||
//TODO: Might have to log out if this happens or somehow refresh the back office UI with a special header maybe?
|
||||
await ReSync(user, identity, actionContext);
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.AllowedSections.UnsortedSequenceEqual(identity.AllowedApplications) == false)
|
||||
{
|
||||
await ReSync(user, identity, actionContext);
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.Groups.Select(x => x.Alias).UnsortedSequenceEqual(identity.Roles) == false)
|
||||
{
|
||||
await ReSync(user, identity, actionContext);
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: This will need to be changed when http://issues.umbraco.org/issue/U4-10173 is merged
|
||||
var startContentIds = user.AllStartContentIds;
|
||||
if (startContentIds.UnsortedSequenceEqual(identity.StartContentNodes) == false)
|
||||
{
|
||||
await ReSync(user, identity, actionContext);
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: This will need to be changed when http://issues.umbraco.org/issue/U4-10173 is merged
|
||||
var startMediaIds = user.AllStartMediaIds;
|
||||
if (startMediaIds.UnsortedSequenceEqual(identity.StartMediaNodes) == false)
|
||||
{
|
||||
await ReSync(user, identity, actionContext);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will update the current request IPrincipal to be correct and re-create the auth ticket
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="identityUser"></param>
|
||||
/// <param name="actionContext"></param>
|
||||
/// <returns></returns>
|
||||
private async Task ReSync(IUser user, UmbracoBackOfficeIdentity identityUser, HttpActionContext actionContext)
|
||||
{
|
||||
var owinCtx = actionContext.Request.TryGetOwinContext().Result;
|
||||
var signInManager = owinCtx.GetBackOfficeSignInManager();
|
||||
|
||||
//ensure the remainder of the request has the correct principal set
|
||||
actionContext.Request.SetPrincipalForRequest(user);
|
||||
|
||||
var backOfficeIdentityUser = Mapper.Map<BackOfficeIdentityUser>(user);
|
||||
await signInManager.SignInAsync(backOfficeIdentityUser, isPersistent: true, rememberBrowser: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ namespace Umbraco.Web.WebApi
|
||||
[UmbracoAuthorize]
|
||||
[DisableBrowserCache]
|
||||
[UmbracoWebApiRequireHttps]
|
||||
[VerifyIfUserTicketDataIsStale]
|
||||
public abstract class UmbracoAuthorizedApiController : UmbracoApiController
|
||||
{
|
||||
|
||||
|
||||
Reference in New Issue
Block a user