diff --git a/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs b/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs
index 3d58253dd9..7569b64cb7 100644
--- a/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs
+++ b/src/Umbraco.Core/Extensions/ClaimsIdentityExtensions.cs
@@ -2,8 +2,11 @@
// See LICENSE for more details.
using System;
+using System.Collections.Generic;
+using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
+using Umbraco.Cms.Core;
namespace Umbraco.Extensions
{
@@ -72,5 +75,271 @@ namespace Umbraco.Extensions
return identity.FindFirst(claimType)?.Value;
}
+
+ ///
+ /// Returns the required claim types for a back office identity
+ ///
+ ///
+ /// This does not include the role claim type or allowed apps type since that is a collection and in theory could be empty
+ ///
+ public static IEnumerable RequiredBackOfficeClaimTypes => new[]
+ {
+ ClaimTypes.NameIdentifier, //id
+ ClaimTypes.Name, //username
+ ClaimTypes.GivenName,
+ // Constants.Security.StartContentNodeIdClaimType, These seem to be able to be null...
+ // Constants.Security.StartMediaNodeIdClaimType,
+ ClaimTypes.Locality,
+ Constants.Security.SecurityStampClaimType
+ };
+
+ ///
+ /// Verify that a ClaimsIdentity has all the required claim types
+ ///
+ ///
+ /// Verified identity wrapped in a ClaimsIdentity with BackOfficeAuthentication type
+ /// True if ClaimsIdentity
+ public static bool VerifyBackOfficeIdentity(this ClaimsIdentity identity, out ClaimsIdentity verifiedIdentity)
+ {
+ // Validate that all required claims exist
+ foreach (var claimType in RequiredBackOfficeClaimTypes)
+ {
+ if (identity.HasClaim(x => x.Type == claimType) == false ||
+ identity.HasClaim(x => x.Type == claimType && x.Value.IsNullOrWhiteSpace()))
+ {
+ verifiedIdentity = null;
+ return false;
+ }
+ }
+
+ verifiedIdentity = new ClaimsIdentity(identity.Claims, Constants.Security.BackOfficeAuthenticationType);
+ return true;
+ }
+
+ ///
+ /// Add the required claims to be a BackOffice ClaimsIdentity
+ ///
+ /// this
+ /// The users Id
+ /// Username
+ /// Real name
+ /// Start content nodes
+ /// Start media nodes
+ /// The locality of the user
+ /// Security stamp
+ /// Allowed apps
+ /// Roles
+ public static void AddRequiredClaims(this ClaimsIdentity identity, string userId, string username,
+ string realName, IEnumerable startContentNodes, IEnumerable startMediaNodes, string culture,
+ string securityStamp, IEnumerable allowedApps, IEnumerable roles)
+ {
+ //This is the id that 'identity' uses to check for the user id
+ if (identity.HasClaim(x => x.Type == ClaimTypes.NameIdentifier) == false)
+ {
+ identity.AddClaim(new Claim(
+ ClaimTypes.NameIdentifier,
+ userId,
+ ClaimValueTypes.String,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
+ identity));
+ }
+
+ if (identity.HasClaim(x => x.Type == ClaimTypes.Name) == false)
+ {
+ identity.AddClaim(new Claim(
+ ClaimTypes.Name,
+ username,
+ ClaimValueTypes.String,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
+ identity));
+ }
+
+ if (identity.HasClaim(x => x.Type == ClaimTypes.GivenName) == false)
+ {
+ identity.AddClaim(new Claim(
+ ClaimTypes.GivenName,
+ realName,
+ ClaimValueTypes.String,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
+ identity));
+ }
+
+ if (identity.HasClaim(x => x.Type == Constants.Security.StartContentNodeIdClaimType) == false &&
+ startContentNodes != null)
+ {
+ foreach (var startContentNode in startContentNodes)
+ {
+ identity.AddClaim(new Claim(
+ Constants.Security.StartContentNodeIdClaimType,
+ startContentNode.ToInvariantString(),
+ ClaimValueTypes.Integer32,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
+ identity));
+ }
+ }
+
+ if (identity.HasClaim(x => x.Type == Constants.Security.StartMediaNodeIdClaimType) == false &&
+ startMediaNodes != null)
+ {
+ foreach (var startMediaNode in startMediaNodes)
+ {
+ identity.AddClaim(new Claim(
+ Constants.Security.StartMediaNodeIdClaimType,
+ startMediaNode.ToInvariantString(),
+ ClaimValueTypes.Integer32,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
+ identity));
+ }
+ }
+
+ if (identity.HasClaim(x => x.Type == ClaimTypes.Locality) == false)
+ {
+ identity.AddClaim(new Claim(
+ ClaimTypes.Locality,
+ culture,
+ ClaimValueTypes.String,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
+ identity));
+ }
+
+ // The security stamp claim is also required
+ if (identity.HasClaim(x => x.Type == Constants.Security.SecurityStampClaimType) == false)
+ {
+ identity.AddClaim(new Claim(
+ Constants.Security.SecurityStampClaimType,
+ securityStamp,
+ ClaimValueTypes.String,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
+ identity));
+ }
+
+ // Add each app as a separate claim
+ if (identity.HasClaim(x => x.Type == Constants.Security.AllowedApplicationsClaimType) == false &&
+ allowedApps != null)
+ {
+ foreach (var application in allowedApps)
+ {
+ identity.AddClaim(new Claim(
+ Constants.Security.AllowedApplicationsClaimType,
+ application,
+ ClaimValueTypes.String,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
+ identity));
+ }
+ }
+
+ // Claims are added by the ClaimsIdentityFactory because our UserStore supports roles, however this identity might
+ // not be made with that factory if it was created with a different ticket so perform the check
+ if (identity.HasClaim(x => x.Type == ClaimsIdentity.DefaultRoleClaimType) == false && roles != null)
+ {
+ // Manually add them
+ foreach (var roleName in roles)
+ {
+ identity.AddClaim(new Claim(
+ identity.RoleClaimType,
+ roleName,
+ ClaimValueTypes.String,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
+ identity));
+ }
+ }
+ }
+
+ ///
+ /// Get the start content nodes from a ClaimsIdentity
+ ///
+ ///
+ /// Array of start content nodes
+ public static int[] GetStartContentNodes(this ClaimsIdentity identity) =>
+ identity.FindAll(x => x.Type == Constants.Security.StartContentNodeIdClaimType)
+ .Select(node => int.TryParse(node.Value, out var i) ? i : default)
+ .Where(x => x != default).ToArray();
+
+ ///
+ /// Get the start media nodes from a ClaimsIdentity
+ ///
+ ///
+ /// Array of start media nodes
+ public static int[] GetStartMediaNodes(this ClaimsIdentity identity) =>
+ identity.FindAll(x => x.Type == Constants.Security.StartMediaNodeIdClaimType)
+ .Select(node => int.TryParse(node.Value, out var i) ? i : default)
+ .Where(x => x != default).ToArray();
+
+ ///
+ /// Get the allowed applications from a ClaimsIdentity
+ ///
+ ///
+ ///
+ public static string[] GetAllowedApplications(this ClaimsIdentity identity) => identity
+ .FindAll(x => x.Type == Constants.Security.AllowedApplicationsClaimType).Select(app => app.Value).ToArray();
+
+ ///
+ /// Get the user ID from a ClaimsIdentity
+ ///
+ ///
+ /// User ID as integer
+ public static int GetId(this ClaimsIdentity identity) => int.Parse(identity.FindFirstValue(ClaimTypes.NameIdentifier));
+
+ ///
+ /// Get the real name belonging to the user from a ClaimsIdentity
+ ///
+ ///
+ /// Real name of the user
+ public static string GetRealName(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.GivenName);
+
+ ///
+ /// Get the username of the user from a ClaimsIdentity
+ ///
+ ///
+ /// Username of the user
+ public static string GetUsername(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.Name);
+
+ ///
+ /// Get the culture string from a ClaimsIdentity
+ ///
+ ///
+ /// Culture string
+ public static string GetCultureString(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.Locality);
+
+ ///
+ /// Get the security stamp from a ClaimsIdentity
+ ///
+ ///
+ /// Security stamp
+ public static string GetSecurityStamp(this ClaimsIdentity identity) => identity.FindFirstValue(Constants.Security.SecurityStampClaimType);
+
+ ///
+ /// Get the roles assigned to a user from a ClaimsIdentity
+ ///
+ ///
+ /// Array of roles
+ public static string[] GetRoles(this ClaimsIdentity identity) => identity
+ .FindAll(x => x.Type == ClaimsIdentity.DefaultRoleClaimType).Select(role => role.Value).ToArray();
+
+
+ ///
+ /// Adds or updates and existing claim.
+ ///
+ public static void AddOrUpdateClaim(this ClaimsIdentity identity, Claim claim)
+ {
+ if (identity == null)
+ {
+ throw new ArgumentNullException(nameof(identity));
+ }
+
+ Claim existingClaim = identity.Claims.FirstOrDefault(x => x.Type == claim.Type);
+ identity.TryRemoveClaim(existingClaim);
+
+ identity.AddClaim(claim);
+ }
}
}
diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs
index 15eba68de1..3068b1c41d 100644
--- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs
+++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs
@@ -2,9 +2,9 @@
// See LICENSE for more details.
using System.Globalization;
+using System.Security.Claims;
using System.Security.Principal;
using System.Threading;
-using Umbraco.Cms.Core.Security;
namespace Umbraco.Extensions
{
@@ -24,9 +24,9 @@ namespace Umbraco.Extensions
public static CultureInfo GetCulture(this IIdentity identity)
{
- if (identity is UmbracoBackOfficeIdentity umbIdentity && umbIdentity.IsAuthenticated)
+ if (identity is ClaimsIdentity umbIdentity && umbIdentity.VerifyBackOfficeIdentity(out _) && umbIdentity.IsAuthenticated)
{
- return CultureInfo.GetCultureInfo(umbIdentity.Culture);
+ return CultureInfo.GetCultureInfo(umbIdentity.GetCultureString());
}
return null;
diff --git a/src/Umbraco.Core/Security/ClaimsPrincipalExtensions.cs b/src/Umbraco.Core/Security/ClaimsPrincipalExtensions.cs
index c8405e1216..ce0e0eb774 100644
--- a/src/Umbraco.Core/Security/ClaimsPrincipalExtensions.cs
+++ b/src/Umbraco.Core/Security/ClaimsPrincipalExtensions.cs
@@ -14,29 +14,21 @@ namespace Umbraco.Extensions
public static class ClaimsPrincipalExtensions
{
///
- /// This will return the current back office identity if the IPrincipal is the correct type
+ /// This will return the current back office identity if the IPrincipal is the correct type and authenticated.
///
///
///
- public static UmbracoBackOfficeIdentity GetUmbracoIdentity(this IPrincipal user)
+ public static ClaimsIdentity GetUmbracoIdentity(this IPrincipal user)
{
- // TODO: It would be nice to get rid of this and only rely on Claims, not a strongly typed identity instance
-
- //If it's already a UmbracoBackOfficeIdentity
- if (user.Identity is UmbracoBackOfficeIdentity backOfficeIdentity) return backOfficeIdentity;
-
- //Check if there's more than one identity assigned and see if it's a UmbracoBackOfficeIdentity and use that
- if (user is ClaimsPrincipal claimsPrincipal)
- {
- backOfficeIdentity = claimsPrincipal.Identities.OfType().FirstOrDefault();
- if (backOfficeIdentity != null) return backOfficeIdentity;
- }
-
- //Otherwise convert to a UmbracoBackOfficeIdentity if it's auth'd
+ // Check if the identity is a ClaimsIdentity, and that's it's authenticated and has all required claims.
if (user.Identity is ClaimsIdentity claimsIdentity
&& claimsIdentity.IsAuthenticated
- && UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity, out var umbracoIdentity))
+ && claimsIdentity.VerifyBackOfficeIdentity(out ClaimsIdentity umbracoIdentity))
{
+ if (claimsIdentity.AuthenticationType == Constants.Security.BackOfficeAuthenticationType)
+ {
+ return claimsIdentity;
+ }
return umbracoIdentity;
}
diff --git a/src/Umbraco.Core/Security/UmbracoBackOfficeIdentity.cs b/src/Umbraco.Core/Security/UmbracoBackOfficeIdentity.cs
deleted file mode 100644
index ab959bf2e7..0000000000
--- a/src/Umbraco.Core/Security/UmbracoBackOfficeIdentity.cs
+++ /dev/null
@@ -1,232 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Security.Claims;
-using Umbraco.Extensions;
-
-namespace Umbraco.Cms.Core.Security
-{
- ///
- /// A custom user identity for the Umbraco backoffice
- ///
- [Serializable]
- public class UmbracoBackOfficeIdentity : ClaimsIdentity
- {
- // TODO: Ideally we remove this class and only deal with ClaimsIdentity as a best practice. All things relevant to our own
- // identity are part of claims. This class would essentially become extension methods on a ClaimsIdentity for resolving
- // values from it.
- public static bool FromClaimsIdentity(ClaimsIdentity identity, out UmbracoBackOfficeIdentity backOfficeIdentity)
- {
- // validate that all claims exist
- foreach (var t in RequiredBackOfficeIdentityClaimTypes)
- {
- // if the identity doesn't have the claim, or the claim value is null
- if (identity.HasClaim(x => x.Type == t) == false || identity.HasClaim(x => x.Type == t && x.Value.IsNullOrWhiteSpace()))
- {
- backOfficeIdentity = null;
- return false;
- }
- }
-
- backOfficeIdentity = new UmbracoBackOfficeIdentity(identity);
- return true;
- }
-
- ///
- /// Create a back office identity based on an existing claims identity
- ///
- ///
- private UmbracoBackOfficeIdentity(ClaimsIdentity identity)
- : base(identity.Claims, Constants.Security.BackOfficeAuthenticationType)
- {
- }
-
- ///
- /// Creates a new UmbracoBackOfficeIdentity
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public UmbracoBackOfficeIdentity(string userId, string username, string realName,
- IEnumerable startContentNodes, IEnumerable startMediaNodes, string culture,
- string securityStamp, IEnumerable allowedApps, IEnumerable roles)
- : base(Enumerable.Empty(), Constants.Security.BackOfficeAuthenticationType) //this ctor is used to ensure the IsAuthenticated property is true
- {
- if (allowedApps == null)
- throw new ArgumentNullException(nameof(allowedApps));
- if (string.IsNullOrWhiteSpace(username))
- throw new ArgumentException("Value cannot be null or whitespace.", nameof(username));
- if (string.IsNullOrWhiteSpace(realName))
- throw new ArgumentException("Value cannot be null or whitespace.", nameof(realName));
- if (string.IsNullOrWhiteSpace(culture))
- throw new ArgumentException("Value cannot be null or whitespace.", nameof(culture));
- if (string.IsNullOrWhiteSpace(securityStamp))
- throw new ArgumentException("Value cannot be null or whitespace.", nameof(securityStamp));
- AddRequiredClaims(userId, username, realName, startContentNodes, startMediaNodes, culture, securityStamp, allowedApps, roles);
- }
-
- ///
- /// Creates a new UmbracoBackOfficeIdentity
- ///
- ///
- /// The original identity created by the ClaimsIdentityFactory
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public UmbracoBackOfficeIdentity(ClaimsIdentity childIdentity,
- string userId, string username, string realName,
- IEnumerable startContentNodes, IEnumerable startMediaNodes, string culture,
- string securityStamp, IEnumerable allowedApps, IEnumerable roles)
- : base(childIdentity.Claims, Constants.Security.BackOfficeAuthenticationType)
- {
- if (string.IsNullOrWhiteSpace(username))
- throw new ArgumentException("Value cannot be null or whitespace.", nameof(username));
- if (string.IsNullOrWhiteSpace(realName))
- throw new ArgumentException("Value cannot be null or whitespace.", nameof(realName));
- if (string.IsNullOrWhiteSpace(culture))
- throw new ArgumentException("Value cannot be null or whitespace.", nameof(culture));
- if (string.IsNullOrWhiteSpace(securityStamp))
- throw new ArgumentException("Value cannot be null or whitespace.", nameof(securityStamp));
-
- AddRequiredClaims(userId, username, realName, startContentNodes, startMediaNodes, culture, securityStamp, allowedApps, roles);
- }
-
- public const string Issuer = Constants.Security.BackOfficeAuthenticationType;
-
- ///
- /// Returns the required claim types for a back office identity
- ///
- ///
- /// This does not include the role claim type or allowed apps type since that is a collection and in theory could be empty
- ///
- public static IEnumerable RequiredBackOfficeIdentityClaimTypes => new[]
- {
- ClaimTypes.NameIdentifier, //id
- ClaimTypes.Name, //username
- ClaimTypes.GivenName,
- Constants.Security.StartContentNodeIdClaimType,
- Constants.Security.StartMediaNodeIdClaimType,
- ClaimTypes.Locality,
- Constants.Security.SecurityStampClaimType
- };
-
- ///
- /// Adds claims based on the ctor data
- ///
- private void AddRequiredClaims(string userId, string username, string realName,
- IEnumerable startContentNodes, IEnumerable startMediaNodes, string culture,
- string securityStamp, IEnumerable allowedApps, IEnumerable roles)
- {
- //This is the id that 'identity' uses to check for the user id
- if (HasClaim(x => x.Type == ClaimTypes.NameIdentifier) == false)
- AddClaim(new Claim(ClaimTypes.NameIdentifier, userId, ClaimValueTypes.String, Issuer, Issuer, this));
-
- if (HasClaim(x => x.Type == ClaimTypes.Name) == false)
- AddClaim(new Claim(ClaimTypes.Name, username, ClaimValueTypes.String, Issuer, Issuer, this));
-
- if (HasClaim(x => x.Type == ClaimTypes.GivenName) == false)
- AddClaim(new Claim(ClaimTypes.GivenName, realName, ClaimValueTypes.String, Issuer, Issuer, this));
-
- if (HasClaim(x => x.Type == Constants.Security.StartContentNodeIdClaimType) == false && startContentNodes != null)
- {
- foreach (var startContentNode in startContentNodes)
- {
- AddClaim(new Claim(Constants.Security.StartContentNodeIdClaimType, startContentNode.ToInvariantString(), ClaimValueTypes.Integer32, Issuer, Issuer, this));
- }
- }
-
- if (HasClaim(x => x.Type == Constants.Security.StartMediaNodeIdClaimType) == false && startMediaNodes != null)
- {
- foreach (var startMediaNode in startMediaNodes)
- {
- AddClaim(new Claim(Constants.Security.StartMediaNodeIdClaimType, startMediaNode.ToInvariantString(), ClaimValueTypes.Integer32, Issuer, Issuer, this));
- }
- }
-
- if (HasClaim(x => x.Type == ClaimTypes.Locality) == false)
- AddClaim(new Claim(ClaimTypes.Locality, culture, ClaimValueTypes.String, Issuer, Issuer, this));
-
- //The security stamp claim is also required
- if (HasClaim(x => x.Type == Constants.Security.SecurityStampClaimType) == false)
- AddClaim(new Claim(Constants.Security.SecurityStampClaimType, securityStamp, ClaimValueTypes.String, Issuer, Issuer, this));
-
- //Add each app as a separate claim
- if (HasClaim(x => x.Type == Constants.Security.AllowedApplicationsClaimType) == false && allowedApps != null)
- {
- foreach (var application in allowedApps)
- {
- AddClaim(new Claim(Constants.Security.AllowedApplicationsClaimType, application, ClaimValueTypes.String, Issuer, Issuer, this));
- }
- }
-
- //Claims are added by the ClaimsIdentityFactory because our UserStore supports roles, however this identity might
- // not be made with that factory if it was created with a different ticket so perform the check
- if (HasClaim(x => x.Type == DefaultRoleClaimType) == false && roles != null)
- {
- //manually add them
- foreach (var roleName in roles)
- {
- AddClaim(new Claim(RoleClaimType, roleName, ClaimValueTypes.String, Issuer, Issuer, this));
- }
- }
-
- }
-
- ///
- ///
- /// Gets the type of authenticated identity.
- ///
- ///
- /// The type of authenticated identity. This property always returns "UmbracoBackOffice".
- ///
- public override string AuthenticationType => Issuer;
-
- private int[] _startContentNodes;
- public int[] StartContentNodes => _startContentNodes ?? (_startContentNodes = FindAll(x => x.Type == Constants.Security.StartContentNodeIdClaimType).Select(app => int.TryParse(app.Value, out var i) ? i : default).Where(x => x != default).ToArray());
-
- private int[] _startMediaNodes;
- public int[] StartMediaNodes => _startMediaNodes ?? (_startMediaNodes = FindAll(x => x.Type == Constants.Security.StartMediaNodeIdClaimType).Select(app => int.TryParse(app.Value, out var i) ? i : default).Where(x => x != default).ToArray());
-
- private string[] _allowedApplications;
- public string[] AllowedApplications => _allowedApplications ?? (_allowedApplications = FindAll(x => x.Type == Constants.Security.AllowedApplicationsClaimType).Select(app => app.Value).ToArray());
-
- public int Id => int.Parse(this.FindFirstValue(ClaimTypes.NameIdentifier));
-
- public string RealName => this.FindFirstValue(ClaimTypes.GivenName);
-
- public string Username => this.FindFirstValue(ClaimTypes.Name);
-
- public string Culture => this.FindFirstValue(ClaimTypes.Locality);
-
- public string SecurityStamp => this.FindFirstValue(Constants.Security.SecurityStampClaimType);
-
- public string[] Roles => FindAll(x => x.Type == DefaultRoleClaimType).Select(role => role.Value).ToArray();
-
- ///
- /// Overridden to remove any temporary claims that shouldn't be copied
- ///
- ///
- public override ClaimsIdentity Clone()
- {
- var clone = base.Clone();
-
- foreach (var claim in clone.FindAll(x => x.Type == Constants.Security.TicketExpiresClaimType).ToList())
- clone.RemoveClaim(claim);
-
- return clone;
- }
- }
-}
diff --git a/src/Umbraco.Infrastructure/Compose/AuditEventsComponent.cs b/src/Umbraco.Infrastructure/Compose/AuditEventsComponent.cs
index d4e52ad4ef..8dc25d85eb 100644
--- a/src/Umbraco.Infrastructure/Compose/AuditEventsComponent.cs
+++ b/src/Umbraco.Infrastructure/Compose/AuditEventsComponent.cs
@@ -26,14 +26,22 @@ namespace Umbraco.Core.Compose
private readonly IUserService _userService;
private readonly IEntityService _entityService;
private readonly IIpResolver _ipResolver;
+ private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
private readonly GlobalSettings _globalSettings;
- public AuditEventsComponent(IAuditService auditService, IUserService userService, IEntityService entityService, IIpResolver ipResolver, IOptions globalSettings)
+ public AuditEventsComponent(
+ IAuditService auditService,
+ IUserService userService,
+ IEntityService entityService,
+ IIpResolver ipResolver,
+ IOptions globalSettings,
+ IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_auditService = auditService;
_userService = userService;
_entityService = entityService;
_ipResolver = ipResolver;
+ _backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_globalSettings = globalSettings.Value;
}
@@ -73,7 +81,7 @@ namespace Umbraco.Core.Compose
{
get
{
- var identity = Thread.CurrentPrincipal?.GetUmbracoIdentity();
+ var identity = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser;
var user = identity == null ? null : _userService.GetUserById(Convert.ToInt32(identity.Id));
return user ?? UnknownUser(_globalSettings);
}
diff --git a/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs b/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs
index f5c0e9ab7a..a57a5c1737 100644
--- a/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs
+++ b/src/Umbraco.Infrastructure/Security/BackOfficeClaimsPrincipalFactory.cs
@@ -4,7 +4,7 @@ using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
-using Umbraco.Cms.Core.Security;
+using Umbraco.Extensions;
namespace Umbraco.Core.Security
{
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Security
///
///
- /// Returns a custom and allows flowing claims from the external identity
+ /// Returns a ClaimsIdentity that has the required claims, and allows flowing of claims from external identity
///
public override async Task CreateAsync(BackOfficeIdentityUser user)
{
@@ -43,10 +43,7 @@ namespace Umbraco.Core.Security
baseIdentity.AddClaim(new Claim(claim.ClaimType, claim.ClaimValue));
}
- // TODO: We want to remove UmbracoBackOfficeIdentity and only rely on ClaimsIdentity, once
- // that is done then we'll create a ClaimsIdentity with all of the requirements here instead
- var umbracoIdentity = new UmbracoBackOfficeIdentity(
- baseIdentity,
+ baseIdentity.AddRequiredClaims(
user.Id,
user.UserName,
user.Name,
@@ -57,7 +54,7 @@ namespace Umbraco.Core.Security
user.AllowedSections,
user.Roles.Select(x => x.RoleId).ToArray());
- return new ClaimsPrincipal(umbracoIdentity);
+ return new ClaimsPrincipal(baseIdentity);
}
///
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/UmbracoBackOfficeIdentityTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/UmbracoBackOfficeIdentityTests.cs
index 46332df4b2..881b880c80 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/UmbracoBackOfficeIdentityTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/BackOffice/UmbracoBackOfficeIdentityTests.cs
@@ -39,24 +39,24 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.BackOffice
new Claim(Constants.Security.SecurityStampClaimType, securityStamp, ClaimValueTypes.String, TestIssuer, TestIssuer),
});
- if (!UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity, out UmbracoBackOfficeIdentity backofficeIdentity))
+ if (!claimsIdentity.VerifyBackOfficeIdentity(out ClaimsIdentity verifiedIdentity))
{
Assert.Fail();
}
- Assert.IsNull(backofficeIdentity.Actor);
- Assert.AreEqual(1234, backofficeIdentity.Id);
+ Assert.IsNull(verifiedIdentity.Actor);
+ Assert.AreEqual(1234, verifiedIdentity.GetId());
//// Assert.AreEqual(sessionId, backofficeIdentity.SessionId);
- Assert.AreEqual(securityStamp, backofficeIdentity.SecurityStamp);
- Assert.AreEqual("testing", backofficeIdentity.Username);
- Assert.AreEqual("hello world", backofficeIdentity.RealName);
- Assert.AreEqual(1, backofficeIdentity.StartContentNodes.Length);
- Assert.IsTrue(backofficeIdentity.StartMediaNodes.UnsortedSequenceEqual(new[] { 5543, 5555 }));
- Assert.IsTrue(new[] { "content", "media" }.SequenceEqual(backofficeIdentity.AllowedApplications));
- Assert.AreEqual("en-us", backofficeIdentity.Culture);
- Assert.IsTrue(new[] { "admin" }.SequenceEqual(backofficeIdentity.Roles));
+ Assert.AreEqual(securityStamp, verifiedIdentity.GetSecurityStamp());
+ Assert.AreEqual("testing", verifiedIdentity.GetUsername());
+ Assert.AreEqual("hello world", verifiedIdentity.GetRealName());
+ Assert.AreEqual(1, verifiedIdentity.GetStartContentNodes().Length);
+ Assert.IsTrue(verifiedIdentity.GetStartMediaNodes().UnsortedSequenceEqual(new[] { 5543, 5555 }));
+ Assert.IsTrue(new[] { "content", "media" }.SequenceEqual(verifiedIdentity.GetAllowedApplications()));
+ Assert.AreEqual("en-us", verifiedIdentity.GetCultureString());
+ Assert.IsTrue(new[] { "admin" }.SequenceEqual(verifiedIdentity.GetRoles()));
- Assert.AreEqual(11, backofficeIdentity.Claims.Count());
+ Assert.AreEqual(11, verifiedIdentity.Claims.Count());
}
[Test]
@@ -68,7 +68,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.BackOffice
new Claim(ClaimTypes.Name, "testing", ClaimValueTypes.String, TestIssuer, TestIssuer),
});
- if (UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity, out _))
+ if (claimsIdentity.VerifyBackOfficeIdentity(out _))
{
Assert.Fail();
}
@@ -93,7 +93,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.BackOffice
new Claim(ClaimsIdentity.DefaultRoleClaimType, "admin", ClaimValueTypes.String, TestIssuer, TestIssuer),
});
- if (UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity, out _))
+ if (claimsIdentity.VerifyBackOfficeIdentity(out _))
{
Assert.Fail();
}
@@ -112,8 +112,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.BackOffice
new Claim("TestClaim1", "test", ClaimValueTypes.Integer32, TestIssuer, TestIssuer)
});
- var identity = new UmbracoBackOfficeIdentity(
- claimsIdentity,
+ claimsIdentity.AddRequiredClaims(
"1234",
"testing",
"hello world",
@@ -124,25 +123,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.BackOffice
new[] { "content", "media" },
new[] { "admin" });
- Assert.AreEqual(12, identity.Claims.Count());
- Assert.IsNull(identity.Actor);
- }
-
- [Test]
- public void Clone()
- {
- var securityStamp = Guid.NewGuid().ToString();
-
- var identity = new UmbracoBackOfficeIdentity(
- "1234", "testing", "hello world", new[] { 654 }, new[] { 654 }, "en-us", securityStamp, new[] { "content", "media" }, new[] { "admin" });
-
- // this will be filtered out during cloning
- identity.AddClaim(new Claim(Constants.Security.TicketExpiresClaimType, "test"));
-
- ClaimsIdentity cloned = identity.Clone();
- Assert.IsNull(cloned.Actor);
-
- Assert.AreEqual(10, cloned.Claims.Count());
+ Assert.AreEqual(12, claimsIdentity.Claims.Count());
+ Assert.IsNull(claimsIdentity.Actor);
}
}
}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ClaimsPrincipalExtensionsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ClaimsPrincipalExtensionsTests.cs
index fe688ceb76..f0ec8698ee 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ClaimsPrincipalExtensionsTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Extensions/ClaimsPrincipalExtensionsTests.cs
@@ -5,9 +5,8 @@ using System;
using System.Linq;
using System.Security.Claims;
using NUnit.Framework;
-using Umbraco.Cms.Core.Security;
+using Umbraco.Cms.Core;
using Umbraco.Extensions;
-using Constants = Umbraco.Cms.Core.Constants;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Extensions
{
@@ -17,7 +16,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Extensions
[Test]
public void Get_Remaining_Ticket_Seconds()
{
- var backOfficeIdentity = new UmbracoBackOfficeIdentity(
+ var backOfficeIdentity = new ClaimsIdentity();
+ backOfficeIdentity.AddRequiredClaims(
Constants.Security.SuperUserIdAsString,
"test",
"test",
@@ -27,6 +27,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Extensions
Guid.NewGuid().ToString(),
Enumerable.Empty(),
Enumerable.Empty());
+
var principal = new ClaimsPrincipal(backOfficeIdentity);
var expireSeconds = 99;
@@ -37,16 +38,52 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Extensions
var expires = now.AddSeconds(expireSeconds).ToString("o");
backOfficeIdentity.AddClaim(new Claim(
- Constants.Security.TicketExpiresClaimType,
- expires,
- ClaimValueTypes.DateTime,
- UmbracoBackOfficeIdentity.Issuer,
- UmbracoBackOfficeIdentity.Issuer,
- backOfficeIdentity));
+ Constants.Security.TicketExpiresClaimType,
+ expires,
+ ClaimValueTypes.DateTime,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
+ backOfficeIdentity));
var ticketRemainingSeconds = principal.GetRemainingAuthSeconds(then);
Assert.AreEqual(remainingSeconds, ticketRemainingSeconds);
}
+
+ [Test]
+ public void AddOrUpdateClaim__Should_ensure_a_claim_is_not_added_twice()
+ {
+ var backOfficeIdentity = new ClaimsIdentity();
+ backOfficeIdentity.AddRequiredClaims(
+ Constants.Security.SuperUserIdAsString,
+ "test",
+ "test",
+ Enumerable.Empty(),
+ Enumerable.Empty(),
+ "en-US",
+ Guid.NewGuid().ToString(),
+ Enumerable.Empty(),
+ Enumerable.Empty());
+
+ var expireSeconds = 99;
+
+ DateTimeOffset now = DateTimeOffset.Now;
+
+ var expires = now.AddSeconds(expireSeconds).ToString("o");
+
+
+ var claim = new Claim(
+ Constants.Security.TicketExpiresClaimType,
+ expires,
+ ClaimValueTypes.DateTime,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
+ backOfficeIdentity);
+
+ backOfficeIdentity.AddOrUpdateClaim(claim);
+ backOfficeIdentity.AddOrUpdateClaim(claim);
+
+ Assert.AreEqual(1, backOfficeIdentity.Claims.Count(x=>x.Type == Constants.Security.TicketExpiresClaimType));
+ }
}
}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/BackOffice/BackOfficeClaimsPrincipalFactoryTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/BackOffice/BackOfficeClaimsPrincipalFactoryTests.cs
index f53dc2b7d6..f893d06daa 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/BackOffice/BackOfficeClaimsPrincipalFactoryTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/BackOffice/BackOfficeClaimsPrincipalFactoryTests.cs
@@ -57,13 +57,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.BackOffice
}
[Test]
- public async Task CreateAsync_Should_Create_Principal_With_Umbraco_Identity()
+ public async Task CreateAsync_Should_Create_Principal_With_Claims_Identity()
{
BackOfficeClaimsPrincipalFactory sut = CreateSut();
ClaimsPrincipal claimsPrincipal = await sut.CreateAsync(_testUser);
- var umbracoBackOfficeIdentity = claimsPrincipal.Identity as UmbracoBackOfficeIdentity;
+ var umbracoBackOfficeIdentity = claimsPrincipal.Identity as ClaimsIdentity;
Assert.IsNotNull(umbracoBackOfficeIdentity);
}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeAntiforgeryTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeAntiforgeryTests.cs
index 9b77e6c60d..f3abcabe61 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeAntiforgeryTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Security/BackOfficeAntiforgeryTests.cs
@@ -12,10 +12,8 @@ using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using NUnit.Framework;
using Umbraco.Cms.Core;
-using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Web.BackOffice.Security;
using Umbraco.Extensions;
-using Constants = Umbraco.Cms.Core.Constants;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Security
{
@@ -24,18 +22,21 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Security
{
private HttpContext GetHttpContext()
{
+ var identity = new ClaimsIdentity();
+ identity.AddRequiredClaims(
+ Constants.Security.SuperUserIdAsString,
+ "test",
+ "test",
+ Enumerable.Empty(),
+ Enumerable.Empty(),
+ "en-US",
+ Guid.NewGuid().ToString(),
+ Enumerable.Empty(),
+ Enumerable.Empty());
+
var httpContext = new DefaultHttpContext()
{
- User = new ClaimsPrincipal(new UmbracoBackOfficeIdentity(
- Constants.Security.SuperUserIdAsString,
- "test",
- "test",
- Enumerable.Empty(),
- Enumerable.Empty(),
- "en-US",
- Guid.NewGuid().ToString(),
- Enumerable.Empty(),
- Enumerable.Empty()))
+ User = new ClaimsPrincipal(identity)
};
httpContext.Request.IsHttps = true;
return httpContext;
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
index c2761819ad..9aa9350fba 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
@@ -584,7 +584,7 @@ namespace Umbraco.Tests.PublishedContent
Assert.IsNotNull(result);
Assert.AreEqual(3, result.Length);
- Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1174, 1173, 1046 }));
+ Assert.IsTrue(result.Select(x => ((dynamic)x).GetId()).ContainsAll(new dynamic[] { 1174, 1173, 1046 }));
}
[Test]
@@ -597,7 +597,7 @@ namespace Umbraco.Tests.PublishedContent
Assert.IsNotNull(result);
Assert.AreEqual(2, result.Length);
- Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1046 }));
+ Assert.IsTrue(result.Select(x => ((dynamic)x).GetId()).ContainsAll(new dynamic[] { 1173, 1046 }));
}
[Test]
@@ -710,7 +710,7 @@ namespace Umbraco.Tests.PublishedContent
Assert.IsNotNull(result);
Assert.AreEqual(10, result.Count());
- Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175 }));
+ Assert.IsTrue(result.Select(x => ((dynamic)x).GetId()).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175 }));
}
[Test]
@@ -723,7 +723,7 @@ namespace Umbraco.Tests.PublishedContent
Assert.IsNotNull(result);
Assert.AreEqual(9, result.Count());
- Assert.IsTrue(result.Select(x => ((dynamic)x).Id).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175, 4444 }));
+ Assert.IsTrue(result.Select(x => ((dynamic)x).GetId()).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175, 4444 }));
}
[Test]
diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs
index 53db23ec0a..1561ad5611 100644
--- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs
+++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs
@@ -1,12 +1,11 @@
using System;
+using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Owin;
-using Umbraco.Cms.Core;
-using Umbraco.Cms.Core.Security;
-using Umbraco.Core.Security;
+using Umbraco.Extensions;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
@@ -30,8 +29,18 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
protected override Task AuthenticateCoreAsync()
{
var securityStamp = Guid.NewGuid().ToString();
- var identity = new UmbracoBackOfficeIdentity(
- Constants.Security.SuperUserIdAsString, "admin", "Admin", new[] { -1 }, new[] { -1 }, "en-US", securityStamp, new[] { "content", "media", "members" }, new[] { "admin" });
+
+ var identity = new ClaimsIdentity();
+ identity.AddRequiredClaims(
+ Cms.Core.Constants.Security.SuperUserIdAsString,
+ "admin",
+ "Admin",
+ new[] { -1 },
+ new[] { -1 },
+ "en-US",
+ securityStamp,
+ new[] { "content", "media", "members" },
+ new[] { "admin" });
return Task.FromResult(new AuthenticationTicket(
identity,
diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs
index 517198a8f1..4b62606b5f 100644
--- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs
+++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
+using System.Security.Claims;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
@@ -22,6 +23,7 @@ using Umbraco.Web.Routing;
using Umbraco.Web.WebApi;
using Umbraco.Tests.TestHelpers.Entities;
using Umbraco.Core.Security;
+using Umbraco.Extensions;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
@@ -97,29 +99,29 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
//chuck it into the props since this is what MS does when hosted and it's needed there
request.Properties["MS_HttpContext"] = httpContext;
- var backofficeIdentity = (UmbracoBackOfficeIdentity) owinContext.Authentication.User.Identity;
+ var backofficeIdentity = (ClaimsIdentity) owinContext.Authentication.User.Identity;
var backofficeSecurity = new Mock();
//mock CurrentUser
var groups = new List();
- for (var index = 0; index < backofficeIdentity.Roles.Length; index++)
+ for (var index = 0; index < backofficeIdentity.GetRoles().Length; index++)
{
- var role = backofficeIdentity.Roles[index];
+ var role = backofficeIdentity.GetRoles()[index];
groups.Add(new ReadOnlyUserGroup(index + 1, role, "icon-user", null, null, role, new string[0], new string[0]));
}
var mockUser = MockedUser.GetUserMock();
mockUser.Setup(x => x.IsApproved).Returns(true);
mockUser.Setup(x => x.IsLockedOut).Returns(false);
- mockUser.Setup(x => x.AllowedSections).Returns(backofficeIdentity.AllowedApplications);
+ mockUser.Setup(x => x.AllowedSections).Returns(backofficeIdentity.GetAllowedApplications());
mockUser.Setup(x => x.Groups).Returns(groups);
mockUser.Setup(x => x.Email).Returns("admin@admin.com");
- mockUser.Setup(x => x.Id).Returns((int)backofficeIdentity.Id);
+ mockUser.Setup(x => x.Id).Returns((int)backofficeIdentity.GetId());
mockUser.Setup(x => x.Language).Returns("en");
- mockUser.Setup(x => x.Name).Returns(backofficeIdentity.RealName);
- mockUser.Setup(x => x.StartContentIds).Returns(backofficeIdentity.StartContentNodes);
- mockUser.Setup(x => x.StartMediaIds).Returns(backofficeIdentity.StartMediaNodes);
- mockUser.Setup(x => x.Username).Returns(backofficeIdentity.Username);
+ mockUser.Setup(x => x.Name).Returns(backofficeIdentity.GetRealName());
+ mockUser.Setup(x => x.StartContentIds).Returns(backofficeIdentity.GetStartContentNodes());
+ mockUser.Setup(x => x.StartMediaIds).Returns(backofficeIdentity.GetStartMediaNodes());
+ mockUser.Setup(x => x.Username).Returns(backofficeIdentity.GetUsername());
backofficeSecurity.Setup(x => x.CurrentUser)
.Returns(mockUser.Object);
diff --git a/src/Umbraco.Web.BackOffice/Filters/CheckIfUserTicketDataIsStaleAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/CheckIfUserTicketDataIsStaleAttribute.cs
index 1aa71f149a..b13d9a82a8 100644
--- a/src/Umbraco.Web.BackOffice/Filters/CheckIfUserTicketDataIsStaleAttribute.cs
+++ b/src/Umbraco.Web.BackOffice/Filters/CheckIfUserTicketDataIsStaleAttribute.cs
@@ -112,13 +112,13 @@ namespace Umbraco.Cms.Web.BackOffice.Filters
return;
}
- var identity = actionContext.HttpContext.User.Identity as UmbracoBackOfficeIdentity;
+ var identity = actionContext.HttpContext.User.Identity as ClaimsIdentity;
if (identity == null)
{
return;
}
- Attempt userId = identity.Id.TryConvertTo();
+ Attempt userId = identity.GetId().TryConvertTo();
if (userId == false)
{
return;
@@ -133,23 +133,23 @@ namespace Umbraco.Cms.Web.BackOffice.Filters
// a list of checks to execute, if any of them pass then we resync
var checks = new Func[]
{
- () => user.Username != identity.Username,
+ () => user.Username != identity.GetUsername(),
() =>
{
CultureInfo culture = user.GetUserCulture(_localizedTextService, _globalSettings.Value);
- return culture != null && culture.ToString() != identity.Culture;
+ return culture != null && culture.ToString() != identity.GetCultureString();
},
- () => user.AllowedSections.UnsortedSequenceEqual(identity.AllowedApplications) == false,
- () => user.Groups.Select(x => x.Alias).UnsortedSequenceEqual(identity.Roles) == false,
+ () => user.AllowedSections.UnsortedSequenceEqual(identity.GetAllowedApplications()) == false,
+ () => user.Groups.Select(x => x.Alias).UnsortedSequenceEqual(identity.GetRoles()) == false,
() =>
{
var startContentIds = user.CalculateContentStartNodeIds(_entityService);
- return startContentIds.UnsortedSequenceEqual(identity.StartContentNodes) == false;
+ return startContentIds.UnsortedSequenceEqual(identity.GetStartContentNodes()) == false;
},
() =>
{
var startMediaIds = user.CalculateMediaStartNodeIds(_entityService);
- return startMediaIds.UnsortedSequenceEqual(identity.StartMediaNodes) == false;
+ return startMediaIds.UnsortedSequenceEqual(identity.GetStartMediaNodes()) == false;
}
};
diff --git a/src/Umbraco.Web.BackOffice/Security/BackOfficeSecureDataFormat.cs b/src/Umbraco.Web.BackOffice/Security/BackOfficeSecureDataFormat.cs
index 8df128661b..c365273cbe 100644
--- a/src/Umbraco.Web.BackOffice/Security/BackOfficeSecureDataFormat.cs
+++ b/src/Umbraco.Web.BackOffice/Security/BackOfficeSecureDataFormat.cs
@@ -1,13 +1,13 @@
using System;
using System.Security.Claims;
+using Umbraco.Extensions;
using Microsoft.AspNetCore.Authentication;
-using Umbraco.Cms.Core.Security;
namespace Umbraco.Cms.Web.BackOffice.Security
{
///
- /// Custom secure format that ensures the Identity in the ticket is and not just a ClaimsIdentity
+ /// Custom secure format that ensures the Identity in the ticket is verified
///
internal class BackOfficeSecureDataFormat : ISecureDataFormat
{
@@ -59,11 +59,14 @@ namespace Umbraco.Cms.Web.BackOffice.Security
return null;
}
- if (!UmbracoBackOfficeIdentity.FromClaimsIdentity((ClaimsIdentity)decrypt.Principal.Identity, out var identity))
+ var identity = (ClaimsIdentity)decrypt.Principal.Identity;
+ if (!identity.VerifyBackOfficeIdentity(out ClaimsIdentity verifiedIdentity))
+ {
return null;
+ }
//return the ticket with a UmbracoBackOfficeIdentity
- var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), decrypt.Properties, decrypt.AuthenticationScheme);
+ var ticket = new AuthenticationTicket(new ClaimsPrincipal(verifiedIdentity), decrypt.Properties, decrypt.AuthenticationScheme);
return ticket;
}
diff --git a/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs b/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs
index 03807fd70d..9e9549134e 100644
--- a/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs
+++ b/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs
@@ -12,11 +12,9 @@ using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Net;
using Umbraco.Cms.Core.Routing;
-using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Web;
using Umbraco.Extensions;
-using Constants = Umbraco.Cms.Core.Constants;
namespace Umbraco.Cms.Web.BackOffice.Security
{
@@ -136,7 +134,7 @@ namespace Umbraco.Cms.Web.BackOffice.Security
// Same goes for the signinmanager
IBackOfficeSignInManager signInManager = ctx.HttpContext.RequestServices.GetRequiredService();
- UmbracoBackOfficeIdentity backOfficeIdentity = ctx.Principal.GetUmbracoIdentity();
+ ClaimsIdentity backOfficeIdentity = ctx.Principal.GetUmbracoIdentity();
if (backOfficeIdentity == null)
{
ctx.RejectPrincipal();
@@ -150,33 +148,33 @@ namespace Umbraco.Cms.Web.BackOffice.Security
await securityStampValidator.ValidateAsync(ctx);
EnsureTicketRenewalIfKeepUserLoggedIn(ctx);
- // add a claim to track when the cookie expires, we use this to track time remaining
- backOfficeIdentity.AddClaim(new Claim(
+ // add or update a claim to track when the cookie expires, we use this to track time remaining
+ backOfficeIdentity.AddOrUpdateClaim(new Claim(
Constants.Security.TicketExpiresClaimType,
ctx.Properties.ExpiresUtc.Value.ToString("o"),
ClaimValueTypes.DateTime,
- UmbracoBackOfficeIdentity.Issuer,
- UmbracoBackOfficeIdentity.Issuer,
+ Constants.Security.BackOfficeAuthenticationType,
+ Constants.Security.BackOfficeAuthenticationType,
backOfficeIdentity));
},
OnSigningIn = ctx =>
{
// occurs when sign in is successful but before the ticket is written to the outbound cookie
- UmbracoBackOfficeIdentity backOfficeIdentity = ctx.Principal.GetUmbracoIdentity();
+ ClaimsIdentity backOfficeIdentity = ctx.Principal.GetUmbracoIdentity();
if (backOfficeIdentity != null)
{
// generate a session id and assign it
// create a session token - if we are configured and not in an upgrade state then use the db, otherwise just generate one
Guid session = _runtimeState.Level == RuntimeLevel.Run
- ? _userService.CreateLoginSession(backOfficeIdentity.Id, _ipResolver.GetCurrentRequestIpAddress())
+ ? _userService.CreateLoginSession(backOfficeIdentity.GetId(), _ipResolver.GetCurrentRequestIpAddress())
: Guid.NewGuid();
// add our session claim
- backOfficeIdentity.AddClaim(new Claim(Constants.Security.SessionIdClaimType, session.ToString(), ClaimValueTypes.String, UmbracoBackOfficeIdentity.Issuer, UmbracoBackOfficeIdentity.Issuer, backOfficeIdentity));
+ backOfficeIdentity.AddClaim(new Claim(Constants.Security.SessionIdClaimType, session.ToString(), ClaimValueTypes.String, Constants.Security.BackOfficeAuthenticationType, Constants.Security.BackOfficeAuthenticationType, backOfficeIdentity));
// since it is a cookie-based authentication add that claim
- backOfficeIdentity.AddClaim(new Claim(ClaimTypes.CookiePath, "/", ClaimValueTypes.String, UmbracoBackOfficeIdentity.Issuer, UmbracoBackOfficeIdentity.Issuer, backOfficeIdentity));
+ backOfficeIdentity.AddClaim(new Claim(ClaimTypes.CookiePath, "/", ClaimValueTypes.String, Constants.Security.BackOfficeAuthenticationType, Constants.Security.BackOfficeAuthenticationType, backOfficeIdentity));
}
return Task.CompletedTask;
diff --git a/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs b/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs
index 6579c69536..9513eb2ec2 100644
--- a/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs
+++ b/src/Umbraco.Web.Common/Extensions/HttpContextExtensions.cs
@@ -32,7 +32,7 @@ namespace Umbraco.Extensions
///
/// Returns the current back office identity if an admin is authenticated otherwise null
///
- public static UmbracoBackOfficeIdentity GetCurrentIdentity(this HttpContext http)
+ public static ClaimsIdentity GetCurrentIdentity(this HttpContext http)
{
if (http == null) throw new ArgumentNullException(nameof(http));
if (http.User == null) return null; //there's no user at all so no identity
diff --git a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs
index fe939c1547..18029228b4 100644
--- a/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs
+++ b/src/Umbraco.Web.Common/Security/BackOfficeUserManager.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
@@ -176,8 +177,8 @@ namespace Umbraco.Cms.Web.Common.Security
private string GetCurrentUserId(IPrincipal currentUser)
{
- UmbracoBackOfficeIdentity umbIdentity = currentUser?.GetUmbracoIdentity();
- var currentUserId = umbIdentity?.GetUserId() ?? Cms.Core.Constants.Security.SuperUserIdAsString;
+ ClaimsIdentity umbIdentity = currentUser?.GetUmbracoIdentity();
+ var currentUserId = umbIdentity?.GetUserId() ?? Core.Constants.Security.SuperUserIdAsString;
return currentUserId;
}
diff --git a/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs b/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs
index 27dd822f1a..d7a0aeb043 100644
--- a/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs
+++ b/src/Umbraco.Web.Common/Security/BackofficeSecurity.cs
@@ -55,7 +55,7 @@ namespace Umbraco.Cms.Web.Common.Security
public Attempt GetUserId()
{
var identity = _httpContextAccessor.HttpContext?.GetCurrentIdentity();
- return identity == null ? Attempt.Fail() : Attempt.Succeed(identity.Id);
+ return identity == null ? Attempt.Fail() : Attempt.Succeed(identity.GetId());
}
///
diff --git a/src/Umbraco.Web.Common/Security/MemberManager.cs b/src/Umbraco.Web.Common/Security/MemberManager.cs
index 386b1ba231..00bdcbd436 100644
--- a/src/Umbraco.Web.Common/Security/MemberManager.cs
+++ b/src/Umbraco.Web.Common/Security/MemberManager.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Security.Claims;
using System.Security.Principal;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
@@ -39,7 +40,7 @@ namespace Umbraco.Web.Common.Security
private string GetCurrentUserId(IPrincipal currentUser)
{
- UmbracoBackOfficeIdentity umbIdentity = currentUser?.GetUmbracoIdentity();
+ ClaimsIdentity umbIdentity = currentUser?.GetUmbracoIdentity();
var currentUserId = umbIdentity?.GetUserId() ?? Cms.Core.Constants.Security.SuperUserIdAsString;
return currentUserId;
}