From d14aa007ea3aee246378cba62153fac8e29dbdf5 Mon Sep 17 00:00:00 2001 From: Mole Date: Wed, 17 Feb 2021 09:50:27 +0100 Subject: [PATCH] Add extension methods to replace UmbracoBackOfficeIdentity --- .../Security/ClaimsIdentityExtensions.cs | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 src/Umbraco.Core/Security/ClaimsIdentityExtensions.cs diff --git a/src/Umbraco.Core/Security/ClaimsIdentityExtensions.cs b/src/Umbraco.Core/Security/ClaimsIdentityExtensions.cs new file mode 100644 index 0000000000..65e12895cc --- /dev/null +++ b/src/Umbraco.Core/Security/ClaimsIdentityExtensions.cs @@ -0,0 +1,152 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using Umbraco.Core; + +namespace Umbraco.Extensions +{ + public static class ClaimsIdentityExtensions + { + /// + /// 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, + Constants.Security.StartMediaNodeIdClaimType, + ClaimTypes.Locality, + Constants.Security.SecurityStampClaimType + }; + + public const string Issuer = Constants.Security.BackOfficeAuthenticationType; + + /// + /// 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; + } + + 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, + Issuer, Issuer, identity)); + } + + if (identity.HasClaim(x => x.Type == ClaimTypes.Name) == false) + { + identity.AddClaim(new Claim(ClaimTypes.Name, username, ClaimValueTypes.String, Issuer, Issuer, identity)); + } + + if (identity.HasClaim(x => x.Type == ClaimTypes.GivenName) == false) + { + identity.AddClaim(new Claim(ClaimTypes.GivenName, realName, ClaimValueTypes.String, Issuer, Issuer, 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, Issuer, Issuer, identity)); + } + } + + if (identity.HasClaim(x => x.Type == Constants.Security.StartMediaNodeIdClaimType) == false) + { + foreach (var startMediaNode in startMediaNodes) + { + identity.AddClaim(new Claim(Constants.Security.StartMediaNodeIdClaimType, startMediaNode.ToInvariantString(), ClaimValueTypes.Integer32, Issuer, Issuer, identity)); + } + } + + if (identity.HasClaim(x => x.Type == ClaimTypes.Locality) == false) + { + identity.AddClaim(new Claim(ClaimTypes.Locality, culture, ClaimValueTypes.String, Issuer, Issuer, 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, Issuer, Issuer, 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, Issuer, Issuer, 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, Issuer, Issuer, identity)); + } + } + } + + 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(); + + 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(); + + public static string[] GetAllowedApplications(this ClaimsIdentity identity) => identity + .FindAll(x => x.Type == Constants.Security.AllowedApplicationsClaimType).Select(app => app.Value).ToArray(); + + public static int GetId(this ClaimsIdentity identity) => int.Parse(identity.FindFirstValue(ClaimTypes.NameIdentifier)); + + public static string GetRealName(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.GivenName); + + public static string GetUsername(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.Name); + + public static string GetCulture(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.Locality); + + public static string GetSecurityStamp(this ClaimsIdentity identity) => identity.FindFirstValue(Constants.Security.SecurityStampClaimType); + + public static string[] GetRoles(this ClaimsIdentity identity) => identity + .FindAll(x => x.Type == ClaimsIdentity.DefaultRoleClaimType).Select(role => role.Value).ToArray(); + } +}