Published members cleanup (#10159)
* Getting new netcore PublicAccessChecker in place * Adds full test coverage for PublicAccessChecker * remove PublicAccessComposer * adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller * Implements the required methods on IMemberManager, removes old migrated code * Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops * adds note * adds note * Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling. * Changes name to IUmbracoEndpointBuilder * adds note * Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect * fixing build * Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker * Merges PR * Fixes up build and notes * Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles. * Security stamp for members is now working * Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware. * adds note * removes unused filter, fixes build * fixes WebPath and tests * Looks up entities in one query * remove usings * Fix test, remove stylesheet * Set status code before we write to response to avoid error * Ensures that users and members are validated when logging in. Shares more code between users and members. * merge changes * oops * Reducing and removing published member cache * Fixes RepositoryCacheKeys to ensure the keys are normalized * oops didn't mean to commit this * Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy * oops didn't mean to comit this * bah, far out this keeps getting recommitted. sorry * cannot inject IPublishedMemberCache and cannot have IPublishedMember * splits out files, fixes build * fix tests * removes membership provider classes * removes membership provider classes * updates the identity map definition * reverts commented out lines * reverts commented out lines Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
namespace Umbraco.Cms.Core
|
||||
namespace Umbraco.Cms.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
@@ -130,10 +130,6 @@
|
||||
/// </summary>
|
||||
public static readonly string InternalRolePrefix = "__umbracoRole";
|
||||
|
||||
public static readonly string UmbracoMemberProviderName = "UmbracoMembershipProvider";
|
||||
|
||||
public static readonly string UmbracoRoleProviderName = "UmbracoRoleProvider";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the Comments on a Member
|
||||
/// </summary>
|
||||
@@ -277,7 +273,7 @@
|
||||
/// Developers should not manually use these relation types since they will all be cleared whenever an entity
|
||||
/// (content, media or member) is saved since they are auto-populated based on property values.
|
||||
/// </remarks>
|
||||
public static string[] AutomaticRelationTypes = new[] { RelatedMediaAlias, RelatedDocumentAlias };
|
||||
public static string[] AutomaticRelationTypes { get; } = new[] { RelatedMediaAlias, RelatedDocumentAlias };
|
||||
|
||||
//TODO: return a list of built in types so we can use that to prevent deletion in the uI
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ namespace Umbraco.Cms.Core
|
||||
{
|
||||
public static class Security
|
||||
{
|
||||
public const string UserMembershipProviderName = "UsersMembershipProvider";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the identifier of the 'super' user.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models.PublishedContent
|
||||
{
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Represents a published content item.
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
|
||||
namespace Umbraco.Cms.Core.PublishedCache
|
||||
{
|
||||
// TODO: Kill this, why do we want this at all?
|
||||
// See https://dev.azure.com/umbraco/D-Team%20Tracker/_workitems/edit/11487
|
||||
public interface IPublishedMemberCache : IXPathNavigable
|
||||
public interface IPublishedMemberCache
|
||||
{
|
||||
IPublishedContent GetByProviderKey(object key);
|
||||
IPublishedContent GetById(int memberId);
|
||||
IPublishedContent GetByUsername(string username);
|
||||
IPublishedContent GetByEmail(string email);
|
||||
IPublishedContent GetByMember(IMember member);
|
||||
|
||||
XPathNavigator CreateNavigator(bool preview);
|
||||
|
||||
// if the node does not exist, return null
|
||||
XPathNavigator CreateNodeNavigator(int id, bool preview);
|
||||
/// <summary>
|
||||
/// Get an <see cref="IPublishedContent"/> from an <see cref="IMember"/>
|
||||
/// </summary>
|
||||
/// <param name="member"></param>
|
||||
/// <returns></returns>
|
||||
IPublishedContent Get(IMember member);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a content type identified by its unique identifier.
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -9,19 +11,22 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
|
||||
[DefaultPropertyValueConverter]
|
||||
public class MemberPickerValueConverter : PropertyValueConverterBase
|
||||
{
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
|
||||
public MemberPickerValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IUmbracoContextAccessor umbracoContextAccessor)
|
||||
public MemberPickerValueConverter(
|
||||
IMemberService memberService,
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor,
|
||||
IUmbracoContextAccessor umbracoContextAccessor)
|
||||
{
|
||||
_publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor));
|
||||
_memberService = memberService;
|
||||
_publishedSnapshotAccessor = publishedSnapshotAccessor;
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
}
|
||||
|
||||
public override bool IsConverter(IPublishedPropertyType propertyType)
|
||||
{
|
||||
return propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.MemberPicker);
|
||||
}
|
||||
=> propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.MemberPicker);
|
||||
|
||||
public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType)
|
||||
=> PropertyCacheLevel.Snapshot;
|
||||
@@ -45,24 +50,43 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
|
||||
public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel cacheLevel, object source, bool preview)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_umbracoContextAccessor.UmbracoContext != null)
|
||||
{
|
||||
IPublishedContent member;
|
||||
if (source is int id)
|
||||
{
|
||||
member = _publishedSnapshotAccessor.PublishedSnapshot.Members.GetById(id);
|
||||
IMember m = _memberService.GetById(id);
|
||||
if (m == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
member = _publishedSnapshotAccessor.PublishedSnapshot?.Members.Get(m);
|
||||
if (member != null)
|
||||
{
|
||||
return member;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var sourceUdi = source as GuidUdi;
|
||||
if (sourceUdi == null) return null;
|
||||
member = _publishedSnapshotAccessor.PublishedSnapshot.Members.GetByProviderKey(sourceUdi.Guid);
|
||||
|
||||
IMember m = _memberService.GetByKey(sourceUdi.Guid);
|
||||
if (m == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
member = _publishedSnapshotAccessor.PublishedSnapshot?.Members.Get(m);
|
||||
|
||||
if (member != null)
|
||||
{
|
||||
return member;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -19,6 +20,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
|
||||
{
|
||||
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly IMemberService _memberService;
|
||||
|
||||
private static readonly List<string> PropertiesToExclude = new List<string>
|
||||
{
|
||||
@@ -26,10 +28,14 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
|
||||
Constants.Conventions.Content.Redirect.ToLower(CultureInfo.InvariantCulture)
|
||||
};
|
||||
|
||||
public MultiNodeTreePickerValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, IUmbracoContextAccessor umbracoContextAccessor)
|
||||
public MultiNodeTreePickerValueConverter(
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor,
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
IMemberService memberService)
|
||||
{
|
||||
_publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor));
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
_memberService = memberService;
|
||||
}
|
||||
|
||||
public override bool IsConverter(IPublishedPropertyType propertyType)
|
||||
@@ -96,7 +102,16 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters
|
||||
multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Media, id => _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(guidUdi.Guid));
|
||||
break;
|
||||
case Constants.UdiEntityType.Member:
|
||||
multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Member, id => _publishedSnapshotAccessor.PublishedSnapshot.Members.GetByProviderKey(guidUdi.Guid));
|
||||
multiNodeTreePickerItem = GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Member, id =>
|
||||
{
|
||||
IMember m = _memberService.GetByKey(guidUdi.Guid);
|
||||
if (m == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
IPublishedContent member = _publishedSnapshotAccessor.PublishedSnapshot.Members.Get(m);
|
||||
return member;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
@@ -75,8 +75,7 @@ namespace Umbraco.Extensions
|
||||
content = snapshot.Media.GetById(contentId);
|
||||
break;
|
||||
case IndexTypes.Member:
|
||||
content = snapshot.Members.GetById(contentId);
|
||||
break;
|
||||
throw new NotSupportedException("Cannot convert search results to member instances");
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
@@ -8,6 +9,13 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// </summary>
|
||||
public interface IMemberManager : IUmbracoUserManager<MemberIdentityUser>
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the <see cref="IPublishedContent"/> instance for the specified <see cref="MemberIdentityUser"/>
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
IPublishedContent AsPublishedMember(MemberIdentityUser user);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently logged in member if there is one, else returns null
|
||||
/// </summary>
|
||||
|
||||
13
src/Umbraco.Infrastructure/Security/IMemberUserStore.cs
Normal file
13
src/Umbraco.Infrastructure/Security/IMemberUserStore.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom user store that uses Umbraco member data
|
||||
/// </summary>
|
||||
public interface IMemberUserStore : IUserStore<MemberIdentityUser>
|
||||
{
|
||||
IPublishedContent GetPublishedMember(MemberIdentityUser user);
|
||||
}
|
||||
}
|
||||
@@ -63,15 +63,10 @@ namespace Umbraco.Cms.Core.Security
|
||||
});
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -Id -Groups -LockoutEnabled -PhoneNumber -PhoneNumberConfirmed -TwoFactorEnabled
|
||||
// Umbraco.Code.MapAll -Id -LockoutEnabled -PhoneNumber -PhoneNumberConfirmed -TwoFactorEnabled -ConcurrencyStamp -NormalizedEmail -NormalizedUserName -Roles
|
||||
private void Map(IUser source, BackOfficeIdentityUser target)
|
||||
{
|
||||
// well, the ctor has been fixed
|
||||
/*
|
||||
// these two are already set in ctor but BackOfficeIdentityUser ctor is CompletelyBroken
|
||||
target.Id = source.Id;
|
||||
target.Groups = source.Groups.ToArray();
|
||||
*/
|
||||
// NOTE: Groups/Roles are set in the BackOfficeIdentityUser ctor
|
||||
|
||||
target.CalculatedMediaStartNodeIds = source.CalculateMediaStartNodeIds(_entityService, _appCaches);
|
||||
target.CalculatedContentStartNodeIds = source.CalculateContentStartNodeIds(_entityService, _appCaches);
|
||||
@@ -90,14 +85,6 @@ namespace Umbraco.Cms.Core.Security
|
||||
target.IsApproved = source.IsApproved;
|
||||
target.SecurityStamp = source.SecurityStamp;
|
||||
target.LockoutEnd = source.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?)null;
|
||||
|
||||
// this was in AutoMapper but does not have a setter anyways
|
||||
//target.AllowedSections = source.AllowedSections.ToArray(),
|
||||
|
||||
// these were marked as ignored for AutoMapper but don't have a setter anyways
|
||||
//target.Logins =;
|
||||
//target.Claims =;
|
||||
//target.Roles =;
|
||||
}
|
||||
|
||||
// TODO: We need to validate this mapping is OK, we need to get Umbraco.Code working
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
@@ -12,8 +11,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// </summary>
|
||||
public class MemberIdentityUser : UmbracoIdentityUser
|
||||
{
|
||||
private string _comments;
|
||||
private IReadOnlyCollection<IReadOnlyUserGroup> _groups;
|
||||
private string _comments;
|
||||
|
||||
// Custom comparer for enumerables
|
||||
private static readonly DelegateEqualityComparer<IReadOnlyCollection<IReadOnlyUserGroup>> s_groupsComparer = new DelegateEqualityComparer<IReadOnlyCollection<IReadOnlyUserGroup>>(
|
||||
@@ -73,33 +71,6 @@ namespace Umbraco.Cms.Core.Security
|
||||
// No change tracking because the persisted value is readonly
|
||||
public Guid Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user groups
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<IReadOnlyUserGroup> Groups
|
||||
{
|
||||
get => _groups;
|
||||
set
|
||||
{
|
||||
_groups = value.Where(x => x.Alias != null).ToArray();
|
||||
|
||||
var roles = new List<IdentityUserRole<string>>();
|
||||
foreach (IdentityUserRole<string> identityUserRole in _groups.Select(x => new IdentityUserRole<string>
|
||||
{
|
||||
RoleId = x.Alias,
|
||||
UserId = Id
|
||||
}))
|
||||
{
|
||||
roles.Add(identityUserRole);
|
||||
}
|
||||
|
||||
// now reset the collection
|
||||
Roles = roles;
|
||||
|
||||
BeingDirty.SetPropertyValueAndDetectChanges(value, ref _groups, nameof(Groups), s_groupsComparer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias of the member type
|
||||
/// </summary>
|
||||
|
||||
@@ -9,21 +9,25 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A custom user store that uses Umbraco member data
|
||||
/// </summary>
|
||||
public class MemberUserStore : UmbracoUserStore<MemberIdentityUser, UmbracoIdentityRole>
|
||||
public class MemberUserStore : UmbracoUserStore<MemberIdentityUser, UmbracoIdentityRole>, IMemberUserStore
|
||||
{
|
||||
private const string genericIdentityErrorCode = "IdentityErrorUserStore";
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IUmbracoMapper _mapper;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MemberUserStore"/> class for the members identity store
|
||||
@@ -36,12 +40,14 @@ namespace Umbraco.Cms.Core.Security
|
||||
IMemberService memberService,
|
||||
IUmbracoMapper mapper,
|
||||
IScopeProvider scopeProvider,
|
||||
IdentityErrorDescriber describer)
|
||||
: base(describer)
|
||||
IdentityErrorDescriber describer,
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor)
|
||||
: base(describer)
|
||||
{
|
||||
_memberService = memberService ?? throw new ArgumentNullException(nameof(memberService));
|
||||
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
|
||||
_scopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider));
|
||||
_publishedSnapshotAccessor = publishedSnapshotAccessor;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -603,5 +609,14 @@ namespace Umbraco.Cms.Core.Security
|
||||
return anythingChanged;
|
||||
}
|
||||
|
||||
public IPublishedContent GetPublishedMember(MemberIdentityUser user)
|
||||
{
|
||||
IMember member = _memberService.GetByKey(user.Key);
|
||||
if (member == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return _publishedSnapshotAccessor.PublishedSnapshot?.Members.Get(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
@@ -60,20 +60,5 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
{
|
||||
return "NuCache.ContentCache.ContentByRoute[" + DraftOrPub(previewing) + route + LangId(culture) + "]";
|
||||
}
|
||||
|
||||
//public static string ContentCacheRouteByContentStartsWith()
|
||||
//{
|
||||
// return "NuCache.ContentCache.RouteByContent[";
|
||||
//}
|
||||
|
||||
//public static string ContentCacheContentByRouteStartsWith()
|
||||
//{
|
||||
// return "NuCache.ContentCache.ContentByRoute[";
|
||||
//}
|
||||
|
||||
public static string MemberCacheMember(string name, bool previewing, object p)
|
||||
{
|
||||
return "NuCache.MemberCache." + name + "[" + DraftOrPub(previewing) + p + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,148 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Xml.XPath;
|
||||
using Umbraco.Cms.Infrastructure.PublishedCache.Navigable;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
{
|
||||
internal class MemberCache : IPublishedMemberCache, INavigableData
|
||||
internal class MemberCache : IPublishedMemberCache
|
||||
{
|
||||
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
|
||||
|
||||
public readonly IVariationContextAccessor VariationContextAccessor;
|
||||
private readonly IEntityXmlSerializer _entitySerializer;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
private readonly IPublishedModelFactory _publishedModelFactory;
|
||||
private readonly IAppCache _snapshotCache;
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly PublishedContentTypeCache _contentTypeCache;
|
||||
private readonly bool _previewDefault;
|
||||
|
||||
public MemberCache(bool previewDefault, IAppCache snapshotCache, IMemberService memberService, PublishedContentTypeCache contentTypeCache,
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IEntityXmlSerializer entitySerializer,
|
||||
public MemberCache(
|
||||
bool previewDefault,
|
||||
PublishedContentTypeCache contentTypeCache,
|
||||
IPublishedSnapshotAccessor publishedSnapshotAccessor,
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
IPublishedModelFactory publishedModelFactory)
|
||||
{
|
||||
_snapshotCache = snapshotCache;
|
||||
_publishedSnapshotAccessor = publishedSnapshotAccessor;
|
||||
VariationContextAccessor = variationContextAccessor;
|
||||
_entitySerializer = entitySerializer;
|
||||
_variationContextAccessor = variationContextAccessor;
|
||||
_publishedModelFactory = publishedModelFactory;
|
||||
_memberService = memberService;
|
||||
_previewDefault = previewDefault;
|
||||
_contentTypeCache = contentTypeCache;
|
||||
}
|
||||
|
||||
private T GetCacheItem<T>(string cacheKey, Func<T> getCacheItem)
|
||||
where T : class
|
||||
{
|
||||
var cache = _snapshotCache;
|
||||
return cache == null
|
||||
? getCacheItem()
|
||||
: cache.GetCacheItem<T>(cacheKey, getCacheItem);
|
||||
}
|
||||
|
||||
public IPublishedContent GetById(bool preview, int memberId)
|
||||
{
|
||||
return GetById(memberId);
|
||||
}
|
||||
|
||||
public IPublishedContent /*IPublishedMember*/ GetById(int memberId)
|
||||
{
|
||||
return GetCacheItem(CacheKeys.MemberCacheMember("ById", _previewDefault, memberId), () =>
|
||||
{
|
||||
var member = _memberService.GetById(memberId);
|
||||
return member == null
|
||||
? null
|
||||
: PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, VariationContextAccessor, _publishedModelFactory);
|
||||
});
|
||||
}
|
||||
|
||||
private IPublishedContent /*IPublishedMember*/ GetById(IMember member, bool previewing)
|
||||
{
|
||||
return GetCacheItem(CacheKeys.MemberCacheMember("ById", _previewDefault, member.Id), () =>
|
||||
PublishedMember.Create(member, GetContentType(member.ContentTypeId), previewing, _publishedSnapshotAccessor, VariationContextAccessor, _publishedModelFactory));
|
||||
}
|
||||
|
||||
public IPublishedContent /*IPublishedMember*/ GetByProviderKey(object key)
|
||||
{
|
||||
return GetCacheItem(CacheKeys.MemberCacheMember("ByProviderKey", _previewDefault, key), () =>
|
||||
{
|
||||
var member = _memberService.GetByProviderKey(key);
|
||||
return member == null ? null : GetById(member, _previewDefault);
|
||||
});
|
||||
}
|
||||
|
||||
public IPublishedContent /*IPublishedMember*/ GetByUsername(string username)
|
||||
{
|
||||
return GetCacheItem(CacheKeys.MemberCacheMember("ByUsername", _previewDefault, username), () =>
|
||||
{
|
||||
var member = _memberService.GetByUsername(username);
|
||||
return member == null ? null : GetById(member, _previewDefault);
|
||||
});
|
||||
}
|
||||
|
||||
public IPublishedContent /*IPublishedMember*/ GetByEmail(string email)
|
||||
{
|
||||
return GetCacheItem(CacheKeys.MemberCacheMember("ByEmail", _previewDefault, email), () =>
|
||||
{
|
||||
var member = _memberService.GetByEmail(email);
|
||||
return member == null ? null : GetById(member, _previewDefault);
|
||||
});
|
||||
}
|
||||
|
||||
public IPublishedContent /*IPublishedMember*/ GetByMember(IMember member)
|
||||
{
|
||||
return PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, VariationContextAccessor, _publishedModelFactory);
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> GetAtRoot(bool preview)
|
||||
{
|
||||
// because members are flat (not a tree) everything is at root
|
||||
// because we're loading everything... let's just not cache?
|
||||
var members = _memberService.GetAllMembers();
|
||||
return members.Select(m => PublishedMember.Create(m, GetContentType(m.ContentTypeId), preview, _publishedSnapshotAccessor, VariationContextAccessor, _publishedModelFactory));
|
||||
}
|
||||
|
||||
public XPathNavigator CreateNavigator()
|
||||
{
|
||||
var source = new Source(this, false);
|
||||
var navigator = new NavigableNavigator(source);
|
||||
return navigator;
|
||||
}
|
||||
|
||||
public XPathNavigator CreateNavigator(bool preview)
|
||||
{
|
||||
return CreateNavigator();
|
||||
}
|
||||
|
||||
public XPathNavigator CreateNodeNavigator(int id, bool preview)
|
||||
{
|
||||
var result = _memberService.GetById(id);
|
||||
if (result == null) return null;
|
||||
|
||||
var s = _entitySerializer.Serialize(result);
|
||||
var n = s.GetXmlNode();
|
||||
return n.CreateNavigator();
|
||||
}
|
||||
|
||||
#region Content types
|
||||
|
||||
public IPublishedContentType GetContentType(int id)
|
||||
{
|
||||
return _contentTypeCache.Get(PublishedItemType.Member, id);
|
||||
}
|
||||
public IPublishedContentType GetContentType(int id) => _contentTypeCache.Get(PublishedItemType.Member, id);
|
||||
|
||||
public IPublishedContentType GetContentType(string alias)
|
||||
{
|
||||
return _contentTypeCache.Get(PublishedItemType.Member, alias);
|
||||
}
|
||||
public IPublishedContentType GetContentType(string alias) => _contentTypeCache.Get(PublishedItemType.Member, alias);
|
||||
|
||||
public IPublishedContent Get(IMember member)
|
||||
=> PublishedMember.Create(member, GetContentType(member.ContentTypeId), _previewDefault, _publishedSnapshotAccessor, _variationContextAccessor, _publishedModelFactory);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -12,10 +12,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
// note
|
||||
// the whole PublishedMember thing should be refactored because as soon as a member
|
||||
// is wrapped on in a model, the inner IMember and all associated properties are lost
|
||||
internal class PublishedMember : PublishedContent //, IPublishedMember
|
||||
internal class PublishedMember : PublishedContent
|
||||
{
|
||||
private readonly IMember _member;
|
||||
|
||||
private PublishedMember(
|
||||
IMember member,
|
||||
ContentNode contentNode,
|
||||
@@ -25,7 +23,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
IPublishedModelFactory publishedModelFactory)
|
||||
: base(contentNode, contentData, publishedSnapshotAccessor, variationContextAccessor, publishedModelFactory)
|
||||
{
|
||||
_member = member;
|
||||
Member = member;
|
||||
}
|
||||
|
||||
public static IPublishedContent Create(
|
||||
@@ -45,12 +43,19 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
WriterId = member.CreatorId, // what else?
|
||||
Properties = GetPropertyValues(contentType, member)
|
||||
};
|
||||
var n = new ContentNode(member.Id, member.Key,
|
||||
var n = new ContentNode(
|
||||
member.Id,
|
||||
member.Key,
|
||||
contentType,
|
||||
member.Level, member.Path, member.SortOrder,
|
||||
member.Level,
|
||||
member.Path,
|
||||
member.SortOrder,
|
||||
member.ParentId,
|
||||
member.CreateDate, member.CreatorId);
|
||||
return new PublishedMember(member, n, d, publishedSnapshotAccessor, variationContextAccessor, publishedModelFactory).CreateModel(publishedModelFactory);
|
||||
member.CreateDate,
|
||||
member.CreatorId);
|
||||
|
||||
return new PublishedMember(member, n, d, publishedSnapshotAccessor, variationContextAccessor, publishedModelFactory)
|
||||
.CreateModel(publishedModelFactory);
|
||||
}
|
||||
|
||||
private static Dictionary<string, PropertyData[]> GetPropertyValues(IPublishedContentType contentType, IMember member)
|
||||
@@ -99,25 +104,25 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
|
||||
#region IPublishedMember
|
||||
|
||||
public IMember Member => _member;
|
||||
public IMember Member { get; }
|
||||
|
||||
public string Email => _member.Email;
|
||||
public string Email => Member.Email;
|
||||
|
||||
public string UserName => _member.Username;
|
||||
public string UserName => Member.Username;
|
||||
|
||||
public string Comments => _member.Comments;
|
||||
public string Comments => Member.Comments;
|
||||
|
||||
public bool IsApproved => _member.IsApproved;
|
||||
public bool IsApproved => Member.IsApproved;
|
||||
|
||||
public bool IsLockedOut => _member.IsLockedOut;
|
||||
public bool IsLockedOut => Member.IsLockedOut;
|
||||
|
||||
public DateTime LastLockoutDate => _member.LastLockoutDate;
|
||||
public DateTime LastLockoutDate => Member.LastLockoutDate;
|
||||
|
||||
public DateTime CreationDate => _member.CreateDate;
|
||||
public DateTime CreationDate => Member.CreateDate;
|
||||
|
||||
public DateTime LastLoginDate => _member.LastLoginDate;
|
||||
public DateTime LastLoginDate => Member.LastLoginDate;
|
||||
|
||||
public DateTime LastPasswordChangedDate => _member.LastPasswordChangeDate;
|
||||
public DateTime LastPasswordChangedDate => Member.LastPasswordChangeDate;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -1092,7 +1092,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
{
|
||||
ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainCache, Options.Create(_globalSettings), _variationContextAccessor),
|
||||
MediaCache = new MediaCache(previewDefault, mediaSnap, _variationContextAccessor),
|
||||
MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, memberTypeCache, _publishedSnapshotAccessor, _variationContextAccessor, _entitySerializer, _publishedModelFactory),
|
||||
MemberCache = new MemberCache(previewDefault, memberTypeCache, _publishedSnapshotAccessor, _variationContextAccessor, _publishedModelFactory),
|
||||
DomainCache = domainCache,
|
||||
SnapshotCache = snapshotCache,
|
||||
ElementsCache = elementsCache
|
||||
|
||||
@@ -13,6 +13,7 @@ using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Net;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
@@ -40,7 +41,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security
|
||||
_mockMemberService.Object,
|
||||
new UmbracoMapper(new MapDefinitionCollection(new List<IMapDefinition>()), scopeProvider),
|
||||
scopeProvider,
|
||||
new IdentityErrorDescriber());
|
||||
new IdentityErrorDescriber(),
|
||||
Mock.Of<IPublishedSnapshotAccessor>());
|
||||
|
||||
_mockIdentityOptions = new Mock<IOptions<IdentityOptions>>();
|
||||
var idOptions = new IdentityOptions { Lockout = { AllowedForNewUsers = false } };
|
||||
|
||||
@@ -10,6 +10,7 @@ using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
@@ -35,7 +36,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security
|
||||
_mockMemberService.Object,
|
||||
new UmbracoMapper(new MapDefinitionCollection(new List<IMapDefinition>()), mockScopeProvider.Object),
|
||||
mockScopeProvider.Object,
|
||||
new IdentityErrorDescriber());
|
||||
new IdentityErrorDescriber(),
|
||||
Mock.Of<IPublishedSnapshotAccessor>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Security
|
||||
private static Mock<MemberManager> MockMemberManager()
|
||||
=> new Mock<MemberManager>(
|
||||
Mock.Of<IIpResolver>(),
|
||||
Mock.Of<IUserStore<MemberIdentityUser>>(),
|
||||
Mock.Of<IMemberUserStore>(),
|
||||
Options.Create(new IdentityOptions()),
|
||||
Mock.Of<IPasswordHasher<MemberIdentityUser>>(),
|
||||
Enumerable.Empty<IUserValidator<MemberIdentityUser>>(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
@@ -67,7 +67,7 @@ namespace Umbraco.Tests.Cache.PublishedCache
|
||||
var publishedShapshot = new PublishedSnapshot(
|
||||
new PublishedContentCache(xmlStore, domainCache, appCache, globalSettings, ContentTypesCache, null, VariationContextAccessor, null),
|
||||
new PublishedMediaCache(xmlStore, Mock.Of<IMediaService>(), Mock.Of<IUserService>(), appCache, ContentTypesCache, Factory.GetRequiredService<IEntityXmlSerializer>(), umbracoContextAccessor, VariationContextAccessor),
|
||||
new PublishedMemberCache(null, appCache, Mock.Of<IMemberService>(), ContentTypesCache, Mock.Of<IUserService>(), VariationContextAccessor),
|
||||
new PublishedMemberCache(ContentTypesCache, VariationContextAccessor),
|
||||
domainCache);
|
||||
var publishedSnapshotService = new Mock<IPublishedSnapshotService>();
|
||||
publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny<string>())).Returns(publishedShapshot);
|
||||
|
||||
@@ -7,7 +7,7 @@ using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PublishedCache
|
||||
namespace Umbraco.Tests.LegacyXmlPublishedCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Exposes a member object as IPublishedContent
|
||||
@@ -18,18 +18,15 @@ namespace Umbraco.Cms.Core.PublishedCache
|
||||
private readonly IMembershipUser _membershipUser;
|
||||
private readonly IPublishedProperty[] _properties;
|
||||
private readonly IPublishedContentType _publishedMemberType;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public PublishedMember(
|
||||
IMember member,
|
||||
IPublishedContentType publishedMemberType,
|
||||
IUserService userService,
|
||||
IVariationContextAccessor variationContextAccessor) : base(variationContextAccessor)
|
||||
{
|
||||
_member = member ?? throw new ArgumentNullException(nameof(member));
|
||||
_membershipUser = member;
|
||||
_publishedMemberType = publishedMemberType ?? throw new ArgumentNullException(nameof(publishedMemberType));
|
||||
_userService = userService ?? throw new ArgumentNullException(nameof(userService));
|
||||
|
||||
// RawValueProperty is used for two things here
|
||||
// - for the 'map properties' thing that we should really get rid of
|
||||
@@ -150,6 +147,8 @@ namespace Umbraco.Cms.Core.PublishedCache
|
||||
|
||||
public override int Level => _member.Level;
|
||||
|
||||
public DateTime LastPasswordChangedDate => throw new NotImplementedException();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,136 +1,34 @@
|
||||
using System.Text;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Security;
|
||||
|
||||
namespace Umbraco.Tests.LegacyXmlPublishedCache
|
||||
{
|
||||
class PublishedMemberCache : IPublishedMemberCache
|
||||
{
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IAppCache _requestCache;
|
||||
private readonly XmlStore _xmlStore;
|
||||
private readonly PublishedContentTypeCache _contentTypeCache;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
|
||||
public PublishedMemberCache(XmlStore xmlStore, IAppCache requestCache, IMemberService memberService, PublishedContentTypeCache contentTypeCache, IUserService userService, IVariationContextAccessor variationContextAccessor)
|
||||
public PublishedMemberCache(PublishedContentTypeCache contentTypeCache, IVariationContextAccessor variationContextAccessor)
|
||||
{
|
||||
_requestCache = requestCache;
|
||||
_memberService = memberService;
|
||||
_xmlStore = xmlStore;
|
||||
_contentTypeCache = contentTypeCache;
|
||||
_userService = userService;
|
||||
_variationContextAccessor = variationContextAccessor;
|
||||
}
|
||||
|
||||
public IPublishedContent GetByProviderKey(object key)
|
||||
{
|
||||
return _requestCache.GetCacheItem<IPublishedContent>(
|
||||
GetCacheKey("GetByProviderKey", key), () =>
|
||||
{
|
||||
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
var result = _memberService.GetByProviderKey(key);
|
||||
if (result == null) return null;
|
||||
var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId);
|
||||
return new PublishedMember(result, type, _userService, _variationContextAccessor).CreateModel(Current.PublishedModelFactory);
|
||||
});
|
||||
}
|
||||
|
||||
public IPublishedContent GetById(int memberId)
|
||||
{
|
||||
return _requestCache.GetCacheItem<IPublishedContent>(
|
||||
GetCacheKey("GetById", memberId), () =>
|
||||
{
|
||||
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
var result = _memberService.GetById(memberId);
|
||||
if (result == null) return null;
|
||||
var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId);
|
||||
return new PublishedMember(result, type, _userService, _variationContextAccessor).CreateModel(Current.PublishedModelFactory);
|
||||
});
|
||||
}
|
||||
|
||||
public IPublishedContent GetByUsername(string username)
|
||||
{
|
||||
return _requestCache.GetCacheItem<IPublishedContent>(
|
||||
GetCacheKey("GetByUsername", username), () =>
|
||||
{
|
||||
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
var result = _memberService.GetByUsername(username);
|
||||
if (result == null) return null;
|
||||
var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId);
|
||||
return new PublishedMember(result, type, _userService, _variationContextAccessor).CreateModel(Current.PublishedModelFactory);
|
||||
});
|
||||
}
|
||||
|
||||
public IPublishedContent GetByEmail(string email)
|
||||
{
|
||||
return _requestCache.GetCacheItem<IPublishedContent>(
|
||||
GetCacheKey("GetByEmail", email), () =>
|
||||
{
|
||||
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
|
||||
|
||||
var result = _memberService.GetByEmail(email);
|
||||
if (result == null) return null;
|
||||
var type = _contentTypeCache.Get(PublishedItemType.Member, result.ContentTypeId);
|
||||
return new PublishedMember(result, type, _userService, _variationContextAccessor).CreateModel(Current.PublishedModelFactory);
|
||||
});
|
||||
}
|
||||
|
||||
public IPublishedContent GetByMember(IMember member)
|
||||
public IPublishedContent Get(IMember member)
|
||||
{
|
||||
var type = _contentTypeCache.Get(PublishedItemType.Member, member.ContentTypeId);
|
||||
return new PublishedMember(member, type, _userService, _variationContextAccessor).CreateModel(Current.PublishedModelFactory);
|
||||
}
|
||||
|
||||
public XPathNavigator CreateNavigator()
|
||||
{
|
||||
var doc = _xmlStore.GetMemberXml();
|
||||
return doc.CreateNavigator();
|
||||
}
|
||||
|
||||
public XPathNavigator CreateNavigator(bool preview)
|
||||
{
|
||||
return CreateNavigator();
|
||||
}
|
||||
|
||||
public XPathNavigator CreateNodeNavigator(int id, bool preview)
|
||||
{
|
||||
var n = _xmlStore.GetMemberXmlNode(id);
|
||||
return n?.CreateNavigator();
|
||||
}
|
||||
|
||||
private static string GetCacheKey(string key, params object[] additional)
|
||||
{
|
||||
var sb = new StringBuilder($"{typeof (MembershipHelper).Name}-{key}");
|
||||
foreach (var s in additional)
|
||||
{
|
||||
sb.Append("-");
|
||||
sb.Append(s);
|
||||
}
|
||||
return sb.ToString();
|
||||
return new PublishedMember(member, type, _variationContextAccessor)
|
||||
.CreateModel(Current.PublishedModelFactory);
|
||||
}
|
||||
|
||||
#region Content types
|
||||
|
||||
public IPublishedContentType GetContentType(int id)
|
||||
{
|
||||
return _contentTypeCache.Get(PublishedItemType.Member, id);
|
||||
}
|
||||
public IPublishedContentType GetContentType(int id) => _contentTypeCache.Get(PublishedItemType.Member, id);
|
||||
|
||||
public IPublishedContentType GetContentType(string alias)
|
||||
{
|
||||
return _contentTypeCache.Get(PublishedItemType.Member, alias);
|
||||
}
|
||||
public IPublishedContentType GetContentType(string alias) => _contentTypeCache.Get(PublishedItemType.Member, alias);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
|
||||
private readonly IPublishedContentTypeFactory _publishedContentTypeFactory;
|
||||
private readonly PublishedContentTypeCache _contentTypeCache;
|
||||
private readonly IDomainService _domainService;
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IMediaService _mediaService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IAppCache _requestCache;
|
||||
@@ -104,7 +103,6 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
|
||||
documentRepository, mediaRepository, memberRepository, entitySerializer, hostingEnvironment, hostingLifetime, shortStringHelper);
|
||||
|
||||
_domainService = serviceContext.DomainService;
|
||||
_memberService = serviceContext.MemberService;
|
||||
_mediaService = serviceContext.MediaService;
|
||||
_userService = serviceContext.UserService;
|
||||
_defaultCultureAccessor = defaultCultureAccessor;
|
||||
@@ -134,7 +132,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
|
||||
return new PublishedSnapshot(
|
||||
new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _contentTypeCache, _routesCache, _variationContextAccessor, previewToken),
|
||||
new PublishedMediaCache(_xmlStore, _mediaService, _userService, _requestCache, _contentTypeCache, _entitySerializer, _umbracoContextAccessor, _variationContextAccessor),
|
||||
new PublishedMemberCache(_xmlStore, _requestCache, _memberService, _contentTypeCache, _userService, _variationContextAccessor),
|
||||
new PublishedMemberCache(_contentTypeCache, _variationContextAccessor),
|
||||
domainCache);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.Common;
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Web.Routing;
|
||||
using System.Web.Security;
|
||||
using System.Xml.Linq;
|
||||
using Examine;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@@ -55,7 +54,6 @@ using Umbraco.Cms.Infrastructure.Media;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Mappers;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax;
|
||||
using Umbraco.Cms.Infrastructure.Serialization;
|
||||
using Umbraco.Cms.Tests.Common;
|
||||
@@ -67,7 +65,6 @@ using Umbraco.Web;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Hosting;
|
||||
using Umbraco.Web.Security;
|
||||
using Umbraco.Web.Security.Providers;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
|
||||
namespace Umbraco.Tests.Testing
|
||||
@@ -112,9 +109,9 @@ namespace Umbraco.Tests.Testing
|
||||
|
||||
protected UmbracoTestAttribute Options { get; private set; }
|
||||
|
||||
protected static bool FirstTestInSession = true;
|
||||
protected static bool FirstTestInSession { get; set; } = true;
|
||||
|
||||
protected bool FirstTestInFixture = true;
|
||||
protected bool FirstTestInFixture { get; set; } = true;
|
||||
|
||||
internal TestObjects TestObjects { get; private set; }
|
||||
|
||||
@@ -233,10 +230,6 @@ namespace Umbraco.Tests.Testing
|
||||
|
||||
var memberService = Mock.Of<IMemberService>();
|
||||
var memberTypeService = Mock.Of<IMemberTypeService>();
|
||||
var membershipProvider = new MembersMembershipProvider(memberService, memberTypeService, Mock.Of<IUmbracoVersion>(), TestHelper.GetHostingEnvironment(), TestHelper.GetIpResolver());
|
||||
var membershipHelper = new MembershipHelper(Mock.Of<IHttpContextAccessor>(), Mock.Of<IPublishedMemberCache>(), membershipProvider, Mock.Of<RoleProvider>(), memberService, memberTypeService, Mock.Of<IPublicAccessService>(), AppCaches.Disabled, loggerFactory, ShortStringHelper, Mock.Of<IEntityService>());
|
||||
|
||||
services.AddUnique(membershipHelper);
|
||||
|
||||
TestObjects = new TestObjects();
|
||||
Compose();
|
||||
|
||||
@@ -142,6 +142,7 @@
|
||||
<Compile Include="LegacyXmlPublishedCache\LegacyBackgroundTask\TaskEventArgs.cs" />
|
||||
<Compile Include="LegacyXmlPublishedCache\LegacyBackgroundTask\ThreadingTaskImmutable.cs" />
|
||||
<Compile Include="LegacyXmlPublishedCache\PreviewXmlDto.cs" />
|
||||
<Compile Include="LegacyXmlPublishedCache\PublishedMember.cs" />
|
||||
<Compile Include="Models\ContentXmlTest.cs" />
|
||||
<Compile Include="PublishedContent\SolidPublishedSnapshot.cs" />
|
||||
<Compile Include="Published\ModelTypeTests.cs" />
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace Umbraco.Extensions
|
||||
// then we'll probably have to change this and make it more flexible like how we do for Users. Which means booting up
|
||||
// identity here with the basics and registering all of our own custom services.
|
||||
// Since we are using the defaults in v8 (and below) for members, I think using the default for members now is OK!
|
||||
// TODO: We may need to use services.AddIdentityCore instead if this is doing too much
|
||||
|
||||
services.AddIdentity<MemberIdentityUser, UmbracoIdentityRole>()
|
||||
.AddDefaultTokenProviders()
|
||||
@@ -47,6 +46,7 @@ namespace Umbraco.Extensions
|
||||
|
||||
services.ConfigureOptions<ConfigureMemberIdentityOptions>();
|
||||
|
||||
services.AddScoped<IMemberUserStore>(x => (IMemberUserStore)x.GetRequiredService<IUserStore<MemberIdentityUser>>());
|
||||
services.AddScoped<IPasswordHasher<MemberIdentityUser>, MemberPasswordHasher>();
|
||||
|
||||
services.ConfigureOptions<ConfigureSecurityStampOptions>();
|
||||
|
||||
@@ -11,19 +11,22 @@ using Umbraco.Cms.Core.Net;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Cms.Web.Common.Security
|
||||
{
|
||||
|
||||
public class MemberManager : UmbracoUserManager<MemberIdentityUser, MemberPasswordConfigurationSettings>, IMemberManager
|
||||
{
|
||||
private readonly IMemberUserStore _store;
|
||||
private readonly IPublicAccessService _publicAccessService;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private MemberIdentityUser _currentMember;
|
||||
|
||||
public MemberManager(
|
||||
IIpResolver ipResolver,
|
||||
IUserStore<MemberIdentityUser> store,
|
||||
IMemberUserStore store,
|
||||
IOptions<IdentityOptions> optionsAccessor,
|
||||
IPasswordHasher<MemberIdentityUser> passwordHasher,
|
||||
IEnumerable<IUserValidator<MemberIdentityUser>> userValidators,
|
||||
@@ -37,6 +40,7 @@ namespace Umbraco.Cms.Web.Common.Security
|
||||
: base(ipResolver, store, optionsAccessor, passwordHasher, userValidators, passwordValidators, errors,
|
||||
services, logger, passwordConfiguration)
|
||||
{
|
||||
_store = store;
|
||||
_publicAccessService = publicAccessService;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
@@ -229,5 +233,7 @@ namespace Umbraco.Cms.Web.Common.Security
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public IPublishedContent AsPublishedMember(MemberIdentityUser user) => _store.GetPublishedMember(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,6 @@ namespace Umbraco.Web.Composing
|
||||
public static IUmbracoVersion UmbracoVersion => Factory.GetRequiredService<IUmbracoVersion>();
|
||||
public static IPublishedUrlProvider PublishedUrlProvider => Factory.GetRequiredService<IPublishedUrlProvider>();
|
||||
public static IMenuItemCollectionFactory MenuItemCollectionFactory => Factory.GetRequiredService<IMenuItemCollectionFactory>();
|
||||
public static MembershipHelper MembershipHelper => Factory.GetRequiredService<MembershipHelper>();
|
||||
public static IUmbracoApplicationLifetime UmbracoApplicationLifetime => Factory.GetRequiredService<IUmbracoApplicationLifetime>();
|
||||
public static IPublishedContentQuery PublishedContentQuery => Factory.GetRequiredService<IPublishedContentQuery>();
|
||||
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
using System;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Web.Models.Membership
|
||||
{
|
||||
|
||||
internal class UmbracoMembershipMember : MembershipUser
|
||||
{
|
||||
private readonly IMembershipUser _member;
|
||||
private readonly string _userName;
|
||||
private readonly object _providerUserKey;
|
||||
private readonly bool _isLockedOut;
|
||||
private readonly DateTime _lastLockoutDate;
|
||||
private readonly DateTime _creationDate;
|
||||
private DateTime _lastLoginDate;
|
||||
private readonly DateTime _lastPasswordChangedDate;
|
||||
private readonly string _providerName;
|
||||
private string _email;
|
||||
private string _comment;
|
||||
private bool _isApproved;
|
||||
|
||||
//NOTE: We are only overriding the properties that matter, we don't override things like IsOnline since that is handled with the sub-class and the membership providers.
|
||||
|
||||
//NOTE: We are not calling the base constructor which will validate that a provider with the specified name exists which causes issues with unit tests. The ctor
|
||||
// validation for that doesn't need to be there anyways (have checked the source).
|
||||
public UmbracoMembershipMember(IMembershipUser member, string providerName)
|
||||
{
|
||||
_member = member;
|
||||
//NOTE: We are copying the values here so that everything is consistent with how the underlying built-in ASP.Net membership user
|
||||
// handles data! We don't want to do anything differently there but since we cannot use their ctor we'll need to handle this logic ourselves.
|
||||
if (member.Username != null)
|
||||
_userName = member.Username.Trim();
|
||||
if (member.Email != null)
|
||||
_email = member.Email.Trim();
|
||||
_providerName = providerName;
|
||||
_providerUserKey = member.Key;
|
||||
_comment = member.Comments;
|
||||
_isApproved = member.IsApproved;
|
||||
_isLockedOut = member.IsLockedOut;
|
||||
_creationDate = member.CreateDate.ToUniversalTime();
|
||||
_lastLoginDate = member.LastLoginDate.ToUniversalTime();
|
||||
_lastPasswordChangedDate = member.LastPasswordChangeDate.ToUniversalTime();
|
||||
_lastLockoutDate = member.LastLockoutDate.ToUniversalTime();
|
||||
}
|
||||
|
||||
internal IMembershipUser Member
|
||||
{
|
||||
get { return _member; }
|
||||
}
|
||||
|
||||
public override string UserName
|
||||
{
|
||||
get { return _userName; }
|
||||
}
|
||||
|
||||
public override object ProviderUserKey
|
||||
{
|
||||
get { return _providerUserKey; }
|
||||
}
|
||||
|
||||
public override string Email
|
||||
{
|
||||
get { return _email; }
|
||||
set { _email = value; }
|
||||
}
|
||||
|
||||
public override string PasswordQuestion => string.Empty;
|
||||
|
||||
public override string Comment
|
||||
{
|
||||
get { return _comment; }
|
||||
set { _comment = value; }
|
||||
}
|
||||
|
||||
public override bool IsApproved
|
||||
{
|
||||
get { return _isApproved; }
|
||||
set { _isApproved = value; }
|
||||
}
|
||||
|
||||
public override bool IsLockedOut
|
||||
{
|
||||
get { return _isLockedOut; }
|
||||
}
|
||||
|
||||
public override DateTime LastLockoutDate
|
||||
{
|
||||
get { return _lastLockoutDate; }
|
||||
}
|
||||
|
||||
public override DateTime CreationDate
|
||||
{
|
||||
get { return _creationDate; }
|
||||
}
|
||||
|
||||
public override DateTime LastLoginDate
|
||||
{
|
||||
get { return _lastLoginDate; }
|
||||
set { _lastLoginDate = value; }
|
||||
}
|
||||
|
||||
public override DateTime LastPasswordChangedDate
|
||||
{
|
||||
get { return _lastPasswordChangedDate; }
|
||||
}
|
||||
|
||||
public override string ProviderName
|
||||
{
|
||||
get { return _providerName; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Web.Security;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Security.Providers;
|
||||
|
||||
namespace Umbraco.Web.Security
|
||||
{
|
||||
// MIGRATED TO NETCORE
|
||||
public class MembershipHelper
|
||||
{
|
||||
private readonly MembersMembershipProvider _membershipProvider;
|
||||
private readonly RoleProvider _roleProvider;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IMemberService _memberService;
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
private readonly IPublicAccessService _publicAccessService;
|
||||
private readonly AppCaches _appCaches;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IShortStringHelper _shortStringHelper;
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public MembershipHelper
|
||||
(
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IPublishedMemberCache memberCache,
|
||||
MembersMembershipProvider membershipProvider,
|
||||
RoleProvider roleProvider,
|
||||
IMemberService memberService,
|
||||
IMemberTypeService memberTypeService,
|
||||
IPublicAccessService publicAccessService,
|
||||
AppCaches appCaches,
|
||||
ILoggerFactory loggerFactory,
|
||||
IShortStringHelper shortStringHelper,
|
||||
IEntityService entityService
|
||||
)
|
||||
{
|
||||
MemberCache = memberCache;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_memberService = memberService;
|
||||
_memberTypeService = memberTypeService;
|
||||
_publicAccessService = publicAccessService;
|
||||
_appCaches = appCaches;
|
||||
_loggerFactory = loggerFactory;
|
||||
_logger = _loggerFactory.CreateLogger<MembershipHelper>();
|
||||
_shortStringHelper = shortStringHelper;
|
||||
|
||||
_membershipProvider = membershipProvider ?? throw new ArgumentNullException(nameof(membershipProvider));
|
||||
_roleProvider = roleProvider ?? throw new ArgumentNullException(nameof(roleProvider));
|
||||
_entityService = entityService ?? throw new ArgumentNullException(nameof(entityService));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected IPublishedMemberCache MemberCache { get; }
|
||||
|
||||
#region Querying for front-end
|
||||
|
||||
public virtual IPublishedContent GetByProviderKey(object key)
|
||||
{
|
||||
return MemberCache.GetByProviderKey(key);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<IPublishedContent> GetByProviderKeys(IEnumerable<object> keys)
|
||||
{
|
||||
return keys?.Select(GetByProviderKey).WhereNotNull() ?? Enumerable.Empty<IPublishedContent>();
|
||||
}
|
||||
|
||||
public virtual IPublishedContent GetById(int memberId)
|
||||
{
|
||||
return MemberCache.GetById(memberId);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<IPublishedContent> GetByIds(IEnumerable<int> memberIds)
|
||||
{
|
||||
return memberIds?.Select(GetById).WhereNotNull() ?? Enumerable.Empty<IPublishedContent>();
|
||||
}
|
||||
|
||||
public virtual IPublishedContent GetById(Guid memberId)
|
||||
{
|
||||
return GetByProviderKey(memberId);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<IPublishedContent> GetByIds(IEnumerable<Guid> memberIds)
|
||||
{
|
||||
return GetByProviderKeys(memberIds.OfType<object>());
|
||||
}
|
||||
|
||||
public virtual IPublishedContent GetByUsername(string username)
|
||||
{
|
||||
return MemberCache.GetByUsername(username);
|
||||
}
|
||||
|
||||
public virtual IPublishedContent GetByEmail(string email)
|
||||
{
|
||||
return MemberCache.GetByEmail(email);
|
||||
}
|
||||
|
||||
public virtual IPublishedContent Get(Udi udi)
|
||||
{
|
||||
var guidUdi = udi as GuidUdi;
|
||||
if (guidUdi == null)
|
||||
return null;
|
||||
|
||||
var umbracoType = UdiEntityTypeHelper.ToUmbracoObjectType(udi.EntityType);
|
||||
|
||||
switch (umbracoType)
|
||||
{
|
||||
case UmbracoObjectTypes.Member:
|
||||
// TODO: need to implement Get(guid)!
|
||||
var memberAttempt = _entityService.GetId(guidUdi.Guid, umbracoType);
|
||||
if (memberAttempt.Success)
|
||||
return GetById(memberAttempt.Result);
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,682 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Configuration.Provider;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web.Security;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Composing;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Web.Security
|
||||
{
|
||||
//TODO: Delete - should not be used
|
||||
/// <summary>
|
||||
/// A base membership provider class offering much of the underlying functionality for initializing and password encryption/hashing.
|
||||
/// </summary>
|
||||
[Obsolete("We are now using ASP.NET Core Identity instead of membership providers")]
|
||||
public abstract class MembershipProviderBase : MembershipProvider
|
||||
{
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
|
||||
protected MembershipProviderBase(IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Providers can override this setting, default is 10
|
||||
/// </summary>
|
||||
public virtual int DefaultMinPasswordLength
|
||||
{
|
||||
get { return 10; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Providers can override this setting, default is 0
|
||||
/// </summary>
|
||||
public virtual int DefaultMinNonAlphanumericChars
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Providers can override this setting, default is false to use better security
|
||||
/// </summary>
|
||||
public virtual bool DefaultUseLegacyEncoding
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the raw password value for a given user
|
||||
/// </summary>
|
||||
/// <param name="username"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// By default this will return an invalid attempt, inheritors will need to override this to support it
|
||||
/// </remarks>
|
||||
protected virtual Attempt<string> GetRawPassword(string username)
|
||||
{
|
||||
return Attempt<string>.Fail();
|
||||
}
|
||||
|
||||
private string _applicationName;
|
||||
private bool _enablePasswordReset;
|
||||
private bool _enablePasswordRetrieval;
|
||||
private int _maxInvalidPasswordAttempts;
|
||||
private int _minRequiredNonAlphanumericCharacters;
|
||||
private int _minRequiredPasswordLength;
|
||||
private int _passwordAttemptWindow;
|
||||
private MembershipPasswordFormat _passwordFormat;
|
||||
private string _passwordStrengthRegularExpression;
|
||||
private bool _requiresUniqueEmail;
|
||||
|
||||
public bool UseLegacyEncoding { get; private set; }
|
||||
|
||||
#region Properties
|
||||
|
||||
public string CustomHashAlgorithmType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the membership provider is configured to allow users to reset their passwords.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
/// <returns>true if the membership provider supports password reset; otherwise, false. The default is true.</returns>
|
||||
public override bool EnablePasswordReset
|
||||
{
|
||||
get { return _enablePasswordReset; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the membership provider is configured to allow users to retrieve their passwords.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
/// <returns>true if the membership provider is configured to support password retrieval; otherwise, false. The default is false.</returns>
|
||||
public override bool EnablePasswordRetrieval
|
||||
{
|
||||
get { return _enablePasswordRetrieval; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of invalid password or password-answer attempts allowed before the membership user is locked out.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
/// <returns>The number of invalid password or password-answer attempts allowed before the membership user is locked out.</returns>
|
||||
public override int MaxInvalidPasswordAttempts
|
||||
{
|
||||
get { return _maxInvalidPasswordAttempts; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum number of special characters that must be present in a valid password.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
/// <returns>The minimum number of special characters that must be present in a valid password.</returns>
|
||||
public override int MinRequiredNonAlphanumericCharacters
|
||||
{
|
||||
get { return _minRequiredNonAlphanumericCharacters; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum length required for a password.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
/// <returns>The minimum length required for a password. </returns>
|
||||
public override int MinRequiredPasswordLength
|
||||
{
|
||||
get { return _minRequiredPasswordLength; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
/// <returns>The number of minutes in which a maximum number of invalid password or password-answer attempts are allowed before the membership user is locked out.</returns>
|
||||
public override int PasswordAttemptWindow
|
||||
{
|
||||
get { return _passwordAttemptWindow; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating the format for storing passwords in the membership data store.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
/// <returns>One of the <see cref="T:System.Web.Security.MembershipPasswordFormat"></see> values indicating the format for storing passwords in the data store.</returns>
|
||||
public override MembershipPasswordFormat PasswordFormat
|
||||
{
|
||||
get { return _passwordFormat; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the regular expression used to evaluate a password.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
/// <returns>A regular expression used to evaluate a password.</returns>
|
||||
public override string PasswordStrengthRegularExpression
|
||||
{
|
||||
get { return _passwordStrengthRegularExpression; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always returns false, question/answer is not supported
|
||||
/// </summary>
|
||||
public override bool RequiresQuestionAndAnswer => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the membership provider is configured to require a unique e-mail address for each user name.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
/// <returns>true if the membership provider requires a unique e-mail address; otherwise, false. The default is true.</returns>
|
||||
public override bool RequiresUniqueEmail
|
||||
{
|
||||
get { return _requiresUniqueEmail; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the application using the custom membership provider.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
/// <returns>The name of the application using the custom membership provider.</returns>
|
||||
public override string ApplicationName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _applicationName;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
throw new ProviderException("ApplicationName cannot be empty.");
|
||||
|
||||
if (value.Length > 0x100)
|
||||
throw new ProviderException("Provider application name too long.");
|
||||
|
||||
_applicationName = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the provider.
|
||||
/// </summary>
|
||||
/// <param name="name">The friendly name of the provider.</param>
|
||||
/// <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
|
||||
/// <exception cref="T:System.ArgumentNullException">The name of the provider is null.</exception>
|
||||
/// <exception cref="T:System.InvalidOperationException">An attempt is made to call
|
||||
/// <see cref="M:System.Configuration.Provider.ProviderBase.Initialize(System.String,System.Collections.Specialized.NameValueCollection)"></see> on a provider after the provider
|
||||
/// has already been initialized.</exception>
|
||||
/// <exception cref="T:System.ArgumentException">The name of the provider has a length of zero.</exception>
|
||||
public override void Initialize(string name, NameValueCollection config)
|
||||
{
|
||||
// Initialize base provider class
|
||||
base.Initialize(name, config);
|
||||
|
||||
_enablePasswordRetrieval = config.GetValue("enablePasswordRetrieval", false);
|
||||
_enablePasswordReset = config.GetValue("enablePasswordReset", true);
|
||||
_requiresUniqueEmail = config.GetValue("requiresUniqueEmail", true);
|
||||
_maxInvalidPasswordAttempts = GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0);
|
||||
_passwordAttemptWindow = GetIntValue(config, "passwordAttemptWindow", 10, false, 0);
|
||||
_minRequiredPasswordLength = GetIntValue(config, "minRequiredPasswordLength", DefaultMinPasswordLength, true, 0x80);
|
||||
_minRequiredNonAlphanumericCharacters = GetIntValue(config, "minRequiredNonalphanumericCharacters", DefaultMinNonAlphanumericChars, true, 0x80);
|
||||
_passwordStrengthRegularExpression = config["passwordStrengthRegularExpression"];
|
||||
|
||||
_applicationName = config["applicationName"];
|
||||
if (string.IsNullOrEmpty(_applicationName))
|
||||
_applicationName = GetDefaultAppName(_hostingEnvironment);
|
||||
|
||||
//by default we will continue using the legacy encoding.
|
||||
UseLegacyEncoding = config.GetValue("useLegacyEncoding", DefaultUseLegacyEncoding);
|
||||
|
||||
// make sure password format is Hashed by default.
|
||||
string str = config["passwordFormat"] ?? "Hashed";
|
||||
|
||||
switch (str.ToLower())
|
||||
{
|
||||
case "clear":
|
||||
_passwordFormat = MembershipPasswordFormat.Clear;
|
||||
break;
|
||||
|
||||
case "encrypted":
|
||||
_passwordFormat = MembershipPasswordFormat.Encrypted;
|
||||
break;
|
||||
|
||||
case "hashed":
|
||||
_passwordFormat = MembershipPasswordFormat.Hashed;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ProviderException("Provider bad password format");
|
||||
}
|
||||
|
||||
if ((PasswordFormat == MembershipPasswordFormat.Hashed) && EnablePasswordRetrieval)
|
||||
{
|
||||
var ex = new ProviderException("Provider can not retrieve a hashed password");
|
||||
Current.Logger.LogError(ex, "Cannot specify a Hashed password format with the enabledPasswordRetrieval option set to true");
|
||||
throw ex;
|
||||
}
|
||||
|
||||
CustomHashAlgorithmType = config.GetValue("hashAlgorithmType", string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to ensure the password is valid before raising the event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected override void OnValidatingPassword(ValidatePasswordEventArgs e)
|
||||
{
|
||||
var attempt = IsPasswordValid(e.Password, MinRequiredNonAlphanumericCharacters, PasswordStrengthRegularExpression, MinRequiredPasswordLength);
|
||||
if (attempt.Success == false)
|
||||
{
|
||||
e.Cancel = true;
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnValidatingPassword(e);
|
||||
}
|
||||
|
||||
protected internal enum PasswordValidityError
|
||||
{
|
||||
Ok,
|
||||
Length,
|
||||
AlphanumericChars,
|
||||
Strength
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a request to update the password for a membership user.
|
||||
/// </summary>
|
||||
/// <param name="username">The user to update the password for.</param>
|
||||
/// <param name="oldPassword">Required to change a user password if the user is not new and AllowManuallyChangingPassword is false</param>
|
||||
/// <param name="newPassword">The new password for the specified user.</param>
|
||||
/// <returns>
|
||||
/// true if the password was updated successfully; otherwise, false.
|
||||
/// </returns>
|
||||
public override bool ChangePassword(string username, string oldPassword, string newPassword)
|
||||
{
|
||||
string rawPasswordValue = string.Empty;
|
||||
|
||||
var args = new ValidatePasswordEventArgs(username, newPassword, false);
|
||||
OnValidatingPassword(args);
|
||||
|
||||
if (args.Cancel)
|
||||
{
|
||||
if (args.FailureInformation != null)
|
||||
throw args.FailureInformation;
|
||||
throw new MembershipPasswordException("Change password canceled due to password validation failure.");
|
||||
}
|
||||
|
||||
//Special cases to allow changing password without validating existing credentials
|
||||
// * the member is new and doesn't have a password set
|
||||
// * during installation to set the admin password
|
||||
var installing = Current.RuntimeState.Level == RuntimeLevel.Install;
|
||||
if (rawPasswordValue.StartsWith(Constants.Security.EmptyPasswordPrefix)
|
||||
|| (installing && oldPassword == "default"))
|
||||
{
|
||||
return PerformChangePassword(username, oldPassword, newPassword);
|
||||
}
|
||||
|
||||
if (!oldPassword.IsNullOrWhiteSpace())
|
||||
{
|
||||
if (ValidateUser(username, oldPassword) == false) return false;
|
||||
}
|
||||
|
||||
return PerformChangePassword(username, oldPassword, newPassword);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a request to update the password for a membership user.
|
||||
/// </summary>
|
||||
/// <param name="username">The user to update the password for.</param>
|
||||
/// <param name="oldPassword">This property is ignore for this provider</param>
|
||||
/// <param name="newPassword">The new password for the specified user.</param>
|
||||
/// <returns>
|
||||
/// true if the password was updated successfully; otherwise, false.
|
||||
/// </returns>
|
||||
protected abstract bool PerformChangePassword(string username, string oldPassword, string newPassword);
|
||||
|
||||
/// <summary>
|
||||
/// Processes a request to update the password question and answer for a membership user.
|
||||
/// </summary>
|
||||
/// <param name="username">The user to change the password question and answer for.</param>
|
||||
/// <param name="password">The password for the specified user.</param>
|
||||
/// <param name="newPasswordQuestion">The new password question for the specified user.</param>
|
||||
/// <param name="newPasswordAnswer">The new password answer for the specified user.</param>
|
||||
/// <returns>
|
||||
/// true if the password question and answer are updated successfully; otherwise, false.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Performs the basic validation before passing off to PerformChangePasswordQuestionAndAnswer
|
||||
/// </remarks>
|
||||
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
|
||||
{
|
||||
if (RequiresQuestionAndAnswer == false)
|
||||
{
|
||||
throw new NotSupportedException("Updating the password Question and Answer is not available if requiresQuestionAndAnswer is not set in web.config");
|
||||
}
|
||||
|
||||
if (!password.IsNullOrWhiteSpace())
|
||||
{
|
||||
if (ValidateUser(username, password) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return PerformChangePasswordQuestionAndAnswer(username, password, newPasswordQuestion, newPasswordAnswer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a request to update the password question and answer for a membership user.
|
||||
/// </summary>
|
||||
/// <param name="username">The user to change the password question and answer for.</param>
|
||||
/// <param name="password">The password for the specified user.</param>
|
||||
/// <param name="newPasswordQuestion">The new password question for the specified user.</param>
|
||||
/// <param name="newPasswordAnswer">The new password answer for the specified user.</param>
|
||||
/// <returns>
|
||||
/// true if the password question and answer are updated successfully; otherwise, false.
|
||||
/// </returns>
|
||||
protected abstract bool PerformChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new membership user to the data source.
|
||||
/// </summary>
|
||||
/// <param name="username">The user name for the new user.</param>
|
||||
/// <param name="password">The password for the new user.</param>
|
||||
/// <param name="email">The e-mail address for the new user.</param>
|
||||
/// <param name="passwordQuestion">The password question for the new user.</param>
|
||||
/// <param name="passwordAnswer">The password answer for the new user</param>
|
||||
/// <param name="isApproved">Whether or not the new user is approved to be validated.</param>
|
||||
/// <param name="providerUserKey">The unique identifier from the membership data source for the user.</param>
|
||||
/// <param name="status">A <see cref="T:System.Web.Security.MembershipCreateStatus"></see> enumeration value indicating whether the user was created successfully.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Web.Security.MembershipUser"></see> object populated with the information for the newly created user.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// Ensures the ValidatingPassword event is executed before executing PerformCreateUser and performs basic membership provider validation of values.
|
||||
/// </remarks>
|
||||
public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
|
||||
{
|
||||
var valStatus = ValidateNewUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey);
|
||||
if (valStatus != MembershipCreateStatus.Success)
|
||||
{
|
||||
status = valStatus;
|
||||
return null;
|
||||
}
|
||||
|
||||
return PerformCreateUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the validation of the information for creating a new user
|
||||
/// </summary>
|
||||
/// <param name="username"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <param name="email"></param>
|
||||
/// <param name="passwordQuestion"></param>
|
||||
/// <param name="passwordAnswer"></param>
|
||||
/// <param name="isApproved"></param>
|
||||
/// <param name="providerUserKey"></param>
|
||||
/// <returns></returns>
|
||||
protected MembershipCreateStatus ValidateNewUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey)
|
||||
{
|
||||
var args = new ValidatePasswordEventArgs(username, password, true);
|
||||
OnValidatingPassword(args);
|
||||
if (args.Cancel)
|
||||
{
|
||||
return MembershipCreateStatus.InvalidPassword;
|
||||
}
|
||||
|
||||
// Validate password
|
||||
var passwordValidAttempt = IsPasswordValid(password, MinRequiredNonAlphanumericCharacters, PasswordStrengthRegularExpression, MinRequiredPasswordLength);
|
||||
if (passwordValidAttempt.Success == false)
|
||||
{
|
||||
return MembershipCreateStatus.InvalidPassword;
|
||||
}
|
||||
|
||||
// Validate email
|
||||
if (IsEmailValid(email) == false)
|
||||
{
|
||||
return MembershipCreateStatus.InvalidEmail;
|
||||
}
|
||||
|
||||
// Make sure username isn't all whitespace
|
||||
if (string.IsNullOrWhiteSpace(username.Trim()))
|
||||
{
|
||||
return MembershipCreateStatus.InvalidUserName;
|
||||
}
|
||||
|
||||
// Check password question
|
||||
if (string.IsNullOrWhiteSpace(passwordQuestion) && RequiresQuestionAndAnswer)
|
||||
{
|
||||
return MembershipCreateStatus.InvalidQuestion;
|
||||
}
|
||||
|
||||
// Check password answer
|
||||
if (string.IsNullOrWhiteSpace(passwordAnswer) && RequiresQuestionAndAnswer)
|
||||
{
|
||||
return MembershipCreateStatus.InvalidAnswer;
|
||||
}
|
||||
|
||||
return MembershipCreateStatus.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new membership user to the data source.
|
||||
/// </summary>
|
||||
/// <param name="username">The user name for the new user.</param>
|
||||
/// <param name="password">The password for the new user.</param>
|
||||
/// <param name="email">The e-mail address for the new user.</param>
|
||||
/// <param name="passwordQuestion">The password question for the new user.</param>
|
||||
/// <param name="passwordAnswer">The password answer for the new user</param>
|
||||
/// <param name="isApproved">Whether or not the new user is approved to be validated.</param>
|
||||
/// <param name="providerUserKey">The unique identifier from the membership data source for the user.</param>
|
||||
/// <param name="status">A <see cref="T:System.Web.Security.MembershipCreateStatus"></see> enumeration value indicating whether the user was created successfully.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Web.Security.MembershipUser"></see> object populated with the information for the newly created user.
|
||||
/// </returns>
|
||||
protected abstract MembershipUser PerformCreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the members password if password retrieval is enabled
|
||||
/// </summary>
|
||||
/// <param name="username"></param>
|
||||
/// <param name="answer"></param>
|
||||
/// <returns></returns>
|
||||
public override string GetPassword(string username, string answer)
|
||||
{
|
||||
if (EnablePasswordRetrieval == false)
|
||||
throw new ProviderException("Password Retrieval Not Enabled.");
|
||||
|
||||
if (PasswordFormat == MembershipPasswordFormat.Hashed)
|
||||
throw new ProviderException("Cannot retrieve Hashed passwords.");
|
||||
|
||||
return PerformGetPassword(username, answer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the members password if password retrieval is enabled
|
||||
/// </summary>
|
||||
/// <param name="username"></param>
|
||||
/// <param name="answer"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract string PerformGetPassword(string username, string answer);
|
||||
|
||||
public override string ResetPassword(string username, string answer)
|
||||
{
|
||||
var newPassword = Membership.GeneratePassword(MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters);
|
||||
|
||||
var args = new ValidatePasswordEventArgs(username, newPassword, true);
|
||||
OnValidatingPassword(args);
|
||||
if (args.Cancel)
|
||||
{
|
||||
if (args.FailureInformation != null)
|
||||
{
|
||||
throw args.FailureInformation;
|
||||
}
|
||||
throw new MembershipPasswordException("Reset password canceled due to password validation failure.");
|
||||
}
|
||||
|
||||
return PerformResetPassword(username, answer, newPassword);
|
||||
}
|
||||
|
||||
protected abstract string PerformResetPassword(string username, string answer, string generatedPassword);
|
||||
|
||||
protected internal static Attempt<PasswordValidityError> IsPasswordValid(string password, int minRequiredNonAlphanumericChars, string strengthRegex, int minLength)
|
||||
{
|
||||
if (minRequiredNonAlphanumericChars > 0)
|
||||
{
|
||||
var nonAlphaNumeric = Regex.Replace(password, "[a-zA-Z0-9]", "", RegexOptions.Multiline | RegexOptions.IgnoreCase);
|
||||
if (nonAlphaNumeric.Length < minRequiredNonAlphanumericChars)
|
||||
{
|
||||
return Attempt.Fail(PasswordValidityError.AlphanumericChars);
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(strengthRegex) == false)
|
||||
{
|
||||
if (Regex.IsMatch(password, strengthRegex, RegexOptions.Compiled) == false)
|
||||
{
|
||||
return Attempt.Fail(PasswordValidityError.Strength);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (password.Length < minLength)
|
||||
{
|
||||
return Attempt.Fail(PasswordValidityError.Length);
|
||||
}
|
||||
|
||||
return Attempt.Succeed(PasswordValidityError.Ok);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the default app.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal static string GetDefaultAppName(IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
try
|
||||
{
|
||||
string applicationVirtualPath = hostingEnvironment.ApplicationVirtualPath;
|
||||
if (string.IsNullOrEmpty(applicationVirtualPath))
|
||||
{
|
||||
return "/";
|
||||
}
|
||||
return applicationVirtualPath;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "/";
|
||||
}
|
||||
}
|
||||
|
||||
internal static int GetIntValue(NameValueCollection config, string valueName, int defaultValue, bool zeroAllowed, int maxValueAllowed)
|
||||
{
|
||||
int num;
|
||||
string s = config[valueName];
|
||||
if (s == null)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
if (!int.TryParse(s, out num))
|
||||
{
|
||||
if (zeroAllowed)
|
||||
{
|
||||
throw new ProviderException("Value must be non negative integer");
|
||||
}
|
||||
throw new ProviderException("Value must be positive integer");
|
||||
}
|
||||
if (zeroAllowed && (num < 0))
|
||||
{
|
||||
throw new ProviderException("Value must be non negativeinteger");
|
||||
}
|
||||
if (!zeroAllowed && (num <= 0))
|
||||
{
|
||||
throw new ProviderException("Value must be positive integer");
|
||||
}
|
||||
if ((maxValueAllowed > 0) && (num > maxValueAllowed))
|
||||
{
|
||||
throw new ProviderException("Value too big");
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
internal static bool IsEmailValid(string email)
|
||||
{
|
||||
return new EmailAddressAttribute().IsValid(email);
|
||||
}
|
||||
|
||||
protected internal string DecryptPassword(string pass)
|
||||
{
|
||||
//if we are doing it the old way
|
||||
|
||||
if (UseLegacyEncoding)
|
||||
{
|
||||
return LegacyUnEncodePassword(pass);
|
||||
}
|
||||
|
||||
//This is the correct way to implement this (as per the sql membership provider)
|
||||
|
||||
switch (PasswordFormat)
|
||||
{
|
||||
case MembershipPasswordFormat.Clear:
|
||||
return pass;
|
||||
case MembershipPasswordFormat.Hashed:
|
||||
throw new ProviderException("Provider can not decrypt hashed password");
|
||||
case MembershipPasswordFormat.Encrypted:
|
||||
default:
|
||||
var bytes = DecryptPassword(Convert.FromBase64String(pass));
|
||||
return bytes == null ? null : Encoding.Unicode.GetString(bytes, 16, bytes.Length - 16);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unencode password.
|
||||
/// </summary>
|
||||
/// <param name="encodedPassword">The encoded password.</param>
|
||||
/// <returns>The unencoded password.</returns>
|
||||
protected string LegacyUnEncodePassword(string encodedPassword)
|
||||
{
|
||||
string password = encodedPassword;
|
||||
switch (PasswordFormat)
|
||||
{
|
||||
case MembershipPasswordFormat.Clear:
|
||||
break;
|
||||
case MembershipPasswordFormat.Encrypted:
|
||||
password = Encoding.Unicode.GetString(DecryptPassword(Convert.FromBase64String(password)));
|
||||
break;
|
||||
case MembershipPasswordFormat.Hashed:
|
||||
throw new ProviderException("Cannot unencode a hashed password.");
|
||||
default:
|
||||
throw new ProviderException("Unsupported password format.");
|
||||
}
|
||||
return password;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var result = base.ToString();
|
||||
var sb = new StringBuilder(result);
|
||||
sb.AppendLine("Name =" + Name);
|
||||
sb.AppendLine("_applicationName =" + _applicationName);
|
||||
sb.AppendLine("_enablePasswordReset=" + _enablePasswordReset);
|
||||
sb.AppendLine("_enablePasswordRetrieval=" + _enablePasswordRetrieval);
|
||||
sb.AppendLine("_maxInvalidPasswordAttempts=" + _maxInvalidPasswordAttempts);
|
||||
sb.AppendLine("_minRequiredNonAlphanumericCharacters=" + _minRequiredNonAlphanumericCharacters);
|
||||
sb.AppendLine("_minRequiredPasswordLength=" + _minRequiredPasswordLength);
|
||||
sb.AppendLine("_passwordAttemptWindow=" + _passwordAttemptWindow);
|
||||
sb.AppendLine("_passwordFormat=" + _passwordFormat);
|
||||
sb.AppendLine("_passwordStrengthRegularExpression=" + _passwordStrengthRegularExpression);
|
||||
sb.AppendLine("_requiresQuestionAndAnswer=" + RequiresQuestionAndAnswer);
|
||||
sb.AppendLine("_requiresUniqueEmail=" + _requiresUniqueEmail);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
using System;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Models.Membership;
|
||||
using Umbraco.Web.Security.Providers;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Web.Security
|
||||
{
|
||||
public static class MembershipProviderExtensions
|
||||
{
|
||||
|
||||
internal static UmbracoMembershipMember AsConcreteMembershipUser(this IMembershipUser member, string providerName)
|
||||
{
|
||||
var membershipMember = new UmbracoMembershipMember(member, providerName);
|
||||
return membershipMember;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Method to get the Umbraco Members membership provider based on its alias
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static MembersMembershipProvider GetMembersMembershipProvider()
|
||||
{
|
||||
if (Membership.Providers[Constants.Conventions.Member.UmbracoMemberProviderName] == null)
|
||||
{
|
||||
throw new InvalidOperationException("No membership provider found with name " + Constants.Conventions.Member.UmbracoMemberProviderName);
|
||||
}
|
||||
return (MembersMembershipProvider)Membership.Providers[Constants.Conventions.Member.UmbracoMemberProviderName];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently logged in MembershipUser and flags them as being online - use sparingly (i.e. login)
|
||||
/// </summary>
|
||||
/// <param name="membershipProvider"></param>
|
||||
/// <returns></returns>
|
||||
public static MembershipUser GetCurrentUserOnline(this MembershipProvider membershipProvider)
|
||||
{
|
||||
var username = membershipProvider.GetCurrentUserName();
|
||||
return username.IsNullOrWhiteSpace()
|
||||
? null
|
||||
: membershipProvider.GetUser(username, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the currently logged in MembershipUser
|
||||
/// </summary>
|
||||
/// <param name="membershipProvider"></param>
|
||||
/// <returns></returns>
|
||||
internal static MembershipUser GetCurrentUser(this MembershipProvider membershipProvider)
|
||||
{
|
||||
var username = membershipProvider.GetCurrentUserName();
|
||||
return username.IsNullOrWhiteSpace()
|
||||
? null
|
||||
: membershipProvider.GetUser(username, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Just returns the current user's login name (just a wrapper).
|
||||
/// </summary>
|
||||
/// <param name="membershipProvider"></param>
|
||||
/// <returns></returns>
|
||||
internal static string GetCurrentUserName(this MembershipProvider membershipProvider)
|
||||
{
|
||||
if (Current.HostingEnvironment.IsHosted)
|
||||
{
|
||||
HttpContext current = HttpContext.Current;
|
||||
if (current != null && current.User != null && current.User.Identity != null)
|
||||
return current.User.Identity.Name;
|
||||
}
|
||||
IPrincipal currentPrincipal = Thread.CurrentPrincipal;
|
||||
if (currentPrincipal == null || currentPrincipal.Identity == null)
|
||||
return string.Empty;
|
||||
else
|
||||
return currentPrincipal.Identity.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the provider specified is a built-in Umbraco membership provider
|
||||
/// </summary>
|
||||
/// <param name="membershipProvider"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsUmbracoMembershipProvider(this MembershipProvider membershipProvider)
|
||||
{
|
||||
return (membershipProvider is UmbracoMembershipProviderBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Configuration.Provider;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Net;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Composing;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Web.Security.Providers
|
||||
{
|
||||
//TODO: Delete: should not be used
|
||||
[Obsolete("We are now using ASP.NET Core Identity instead of membership providers")]
|
||||
/// <summary>
|
||||
/// Custom Membership Provider for Umbraco Members (User authentication for Frontend applications NOT umbraco CMS)
|
||||
/// </summary>
|
||||
public class MembersMembershipProvider : UmbracoMembershipProvider<IMembershipMemberService, IMember>
|
||||
{
|
||||
public MembersMembershipProvider()
|
||||
: this(Current.Services.MemberService, Current.Services.MemberTypeService, Current.UmbracoVersion, Current.HostingEnvironment, Current.IpResolver)
|
||||
{ }
|
||||
|
||||
public MembersMembershipProvider(IMembershipMemberService<IMember> memberService, IMemberTypeService memberTypeService, IUmbracoVersion umbracoVersion, IHostingEnvironment hostingEnvironment, IIpResolver ipResolver)
|
||||
: base(memberService, umbracoVersion, hostingEnvironment, ipResolver)
|
||||
{
|
||||
LockPropertyTypeAlias = Constants.Conventions.Member.IsLockedOut;
|
||||
LastLockedOutPropertyTypeAlias = Constants.Conventions.Member.LastLockoutDate;
|
||||
FailedPasswordAttemptsPropertyTypeAlias = Constants.Conventions.Member.FailedPasswordAttempts;
|
||||
ApprovedPropertyTypeAlias = Constants.Conventions.Member.IsApproved;
|
||||
CommentPropertyTypeAlias = Constants.Conventions.Member.Comments;
|
||||
LastLoginPropertyTypeAlias = Constants.Conventions.Member.LastLoginDate;
|
||||
LastPasswordChangedPropertyTypeAlias = Constants.Conventions.Member.LastPasswordChangeDate;
|
||||
_memberTypeService = memberTypeService;
|
||||
}
|
||||
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
private string _defaultMemberTypeAlias = "Member";
|
||||
private volatile bool _hasDefaultMember;
|
||||
private static readonly object Locker = new object();
|
||||
|
||||
public override string ProviderName => "MembersMembershipProvider";
|
||||
|
||||
protected override MembershipUser ConvertToMembershipUser(IMember entity)
|
||||
{
|
||||
return entity.AsConcreteMembershipUser(Name);
|
||||
}
|
||||
|
||||
public string LockPropertyTypeAlias { get; }
|
||||
public string LastLockedOutPropertyTypeAlias { get; }
|
||||
public string FailedPasswordAttemptsPropertyTypeAlias { get; }
|
||||
public string ApprovedPropertyTypeAlias { get; }
|
||||
public string CommentPropertyTypeAlias { get; }
|
||||
public string LastLoginPropertyTypeAlias { get; }
|
||||
public string LastPasswordChangedPropertyTypeAlias { get; }
|
||||
|
||||
public override void Initialize(string name, NameValueCollection config)
|
||||
{
|
||||
base.Initialize(name, config);
|
||||
|
||||
// test for membertype (if not specified, choose the first member type available)
|
||||
if (config["defaultMemberTypeAlias"] != null)
|
||||
{
|
||||
_defaultMemberTypeAlias = config["defaultMemberTypeAlias"];
|
||||
if (_defaultMemberTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new ProviderException("No default member type alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config");
|
||||
}
|
||||
_hasDefaultMember = true;
|
||||
}
|
||||
|
||||
// these need to be lazy else we get a stack overflow since we cannot access Membership.HashAlgorithmType without initializing the providers
|
||||
|
||||
_passwordConfig = new Lazy<IPasswordConfiguration>(() => new MembershipProviderPasswordConfiguration(
|
||||
MinRequiredPasswordLength,
|
||||
MinRequiredNonAlphanumericCharacters > 0,
|
||||
false, false, false, UseLegacyEncoding,
|
||||
CustomHashAlgorithmType.IsNullOrWhiteSpace() ? Membership.HashAlgorithmType : CustomHashAlgorithmType,
|
||||
MaxInvalidPasswordAttempts));
|
||||
|
||||
_passwordSecurity = new Lazy<LegacyPasswordSecurity>(() => new LegacyPasswordSecurity());
|
||||
|
||||
}
|
||||
|
||||
protected override Attempt<string> GetRawPassword(string username)
|
||||
{
|
||||
var found = MemberService.GetByUsername(username);
|
||||
if (found == null) return Attempt<string>.Fail();
|
||||
return Attempt.Succeed(found.RawPasswordValue);
|
||||
}
|
||||
|
||||
public override string DefaultMemberTypeAlias
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_hasDefaultMember == false)
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (_hasDefaultMember == false)
|
||||
{
|
||||
_defaultMemberTypeAlias = _memberTypeService.GetDefault();
|
||||
if (_defaultMemberTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new ProviderException("No default member type alias is specified in the web.config string. Please add a 'defaultUserTypeAlias' to the add element in the provider declaration in web.config");
|
||||
}
|
||||
_hasDefaultMember = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _defaultMemberTypeAlias;
|
||||
}
|
||||
}
|
||||
|
||||
private Lazy<LegacyPasswordSecurity> _passwordSecurity;
|
||||
private Lazy<IPasswordConfiguration> _passwordConfig;
|
||||
|
||||
public override LegacyPasswordSecurity PasswordSecurity => _passwordSecurity.Value;
|
||||
public IPasswordConfiguration PasswordConfiguration => _passwordConfig.Value;
|
||||
|
||||
[Obsolete("We are now using ASP.NET Core Identity instead of membership providers")]
|
||||
private class MembershipProviderPasswordConfiguration : IPasswordConfiguration
|
||||
{
|
||||
public MembershipProviderPasswordConfiguration(int requiredLength, bool requireNonLetterOrDigit, bool requireDigit, bool requireLowercase, bool requireUppercase, bool useLegacyEncoding, string hashAlgorithmType, int maxFailedAccessAttemptsBeforeLockout)
|
||||
{
|
||||
RequiredLength = requiredLength;
|
||||
RequireNonLetterOrDigit = requireNonLetterOrDigit;
|
||||
RequireDigit = requireDigit;
|
||||
RequireLowercase = requireLowercase;
|
||||
RequireUppercase = requireUppercase;
|
||||
UseLegacyEncoding = useLegacyEncoding;
|
||||
HashAlgorithmType = hashAlgorithmType ?? throw new ArgumentNullException(nameof(hashAlgorithmType));
|
||||
MaxFailedAccessAttemptsBeforeLockout = maxFailedAccessAttemptsBeforeLockout;
|
||||
}
|
||||
|
||||
public int RequiredLength { get; }
|
||||
|
||||
public bool RequireNonLetterOrDigit { get; }
|
||||
|
||||
public bool RequireDigit { get; }
|
||||
|
||||
public bool RequireLowercase { get; }
|
||||
|
||||
public bool RequireUppercase { get; }
|
||||
|
||||
public bool UseLegacyEncoding { get; }
|
||||
|
||||
public string HashAlgorithmType { get; }
|
||||
|
||||
public int MaxFailedAccessAttemptsBeforeLockout { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,621 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Configuration.Provider;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web.Configuration;
|
||||
using System.Web.Security;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Net;
|
||||
using Umbraco.Cms.Core.Persistence.Querying;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Web.Composing;
|
||||
|
||||
namespace Umbraco.Web.Security.Providers
|
||||
{
|
||||
//TODO: Delete - should not be used
|
||||
[Obsolete("We are now using ASP.NET Core Identity instead of membership providers")]
|
||||
/// <summary>
|
||||
/// Abstract Membership Provider that users any implementation of IMembershipMemberService{TEntity} service
|
||||
/// </summary>
|
||||
public abstract class UmbracoMembershipProvider<T, TEntity> : UmbracoMembershipProviderBase
|
||||
where T : IMembershipMemberService<TEntity>
|
||||
where TEntity : class, IMembershipUser
|
||||
{
|
||||
private readonly IUmbracoVersion _umbracoVersion;
|
||||
private readonly IIpResolver _ipResolver;
|
||||
|
||||
protected IMembershipMemberService<TEntity> MemberService { get; private set; }
|
||||
|
||||
protected UmbracoMembershipProvider(IMembershipMemberService<TEntity> memberService, IUmbracoVersion umbracoVersion, IHostingEnvironment hostingEnvironment, IIpResolver ipResolver)
|
||||
: base(hostingEnvironment)
|
||||
{
|
||||
_umbracoVersion = umbracoVersion;
|
||||
_ipResolver = ipResolver;
|
||||
MemberService = memberService;
|
||||
}
|
||||
|
||||
public abstract string ProviderName { get; }
|
||||
|
||||
protected abstract MembershipUser ConvertToMembershipUser(TEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the provider.
|
||||
/// </summary>
|
||||
/// <param name="name">The friendly name of the provider.</param>
|
||||
/// <param name="config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
|
||||
/// <exception cref="T:System.ArgumentNullException">The name of the provider is null.</exception>
|
||||
/// <exception cref="T:System.InvalidOperationException">An attempt is made to call
|
||||
/// <see cref="M:System.Configuration.Provider.ProviderBase.Initialize(System.String,System.Collections.Specialized.NameValueCollection)"></see> on a provider after the provider
|
||||
/// has already been initialized.</exception>
|
||||
/// <exception cref="T:System.ArgumentException">The name of the provider has a length of zero.</exception>
|
||||
public override void Initialize(string name, NameValueCollection config)
|
||||
{
|
||||
if (config == null)
|
||||
{ throw new ArgumentNullException("config"); }
|
||||
|
||||
if (string.IsNullOrEmpty(name))
|
||||
name = ProviderName;
|
||||
|
||||
// Initialize base provider class
|
||||
base.Initialize(name, config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a request to update the password for a membership user.
|
||||
/// </summary>
|
||||
/// <param name="username">The user to update the password for.</param>
|
||||
/// <param name="oldPassword">This property is ignore for this provider</param>
|
||||
/// <param name="newPassword">The new password for the specified user.</param>
|
||||
/// <returns>
|
||||
/// true if the password was updated successfully; otherwise, false.
|
||||
/// </returns>
|
||||
protected override bool PerformChangePassword(string username, string oldPassword, string newPassword)
|
||||
{
|
||||
//NOTE: due to backwards compatibility reasons (and UX reasons), this provider doesn't care about the old password and
|
||||
// allows simply setting the password manually so we don't really care about the old password.
|
||||
// This is allowed based on the overridden AllowManuallyChangingPassword option.
|
||||
|
||||
// in order to support updating passwords from the umbraco core, we can't validate the old password
|
||||
var m = MemberService.GetByUsername(username);
|
||||
if (m == null)
|
||||
return false;
|
||||
|
||||
string salt;
|
||||
var encodedPassword = PasswordSecurity.HashNewPassword(Membership.HashAlgorithmType, newPassword, out salt);
|
||||
|
||||
m.RawPasswordValue = PasswordSecurity.FormatPasswordForStorage(Membership.HashAlgorithmType, encodedPassword, salt);
|
||||
m.LastPasswordChangeDate = DateTime.Now;
|
||||
|
||||
MemberService.Save(m);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a request to update the password question and answer for a membership user.
|
||||
/// </summary>
|
||||
/// <param name="username">The user to change the password question and answer for.</param>
|
||||
/// <param name="password">The password for the specified user.</param>
|
||||
/// <param name="newPasswordQuestion">The new password question for the specified user.</param>
|
||||
/// <param name="newPasswordAnswer">The new password answer for the specified user.</param>
|
||||
/// <returns>
|
||||
/// true if the password question and answer are updated successfully; otherwise, false.
|
||||
/// </returns>
|
||||
protected override bool PerformChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
|
||||
{
|
||||
throw new NotSupportedException("Password question/answer is not supported");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new membership user to the data source with the specified member type
|
||||
/// </summary>
|
||||
/// <param name="memberTypeAlias">A specific member type to create the member for</param>
|
||||
/// <param name="username">The user name for the new user.</param>
|
||||
/// <param name="password">The password for the new user.</param>
|
||||
/// <param name="email">The e-mail address for the new user.</param>
|
||||
/// <param name="passwordQuestion">The password question for the new user.</param>
|
||||
/// <param name="passwordAnswer">The password answer for the new user</param>
|
||||
/// <param name="isApproved">Whether or not the new user is approved to be validated.</param>
|
||||
/// <param name="providerUserKey">The unique identifier from the membership data source for the user.</param>
|
||||
/// <param name="status">A <see cref="T:System.Web.Security.MembershipCreateStatus"></see> enumeration value indicating whether the user was created successfully.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Web.Security.MembershipUser"></see> object populated with the information for the newly created user.
|
||||
/// </returns>
|
||||
protected override MembershipUser PerformCreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion,
|
||||
string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
|
||||
{
|
||||
// See if the user already exists
|
||||
if (MemberService.Exists(username))
|
||||
{
|
||||
status = MembershipCreateStatus.DuplicateUserName;
|
||||
Current.Logger.LogWarning("Cannot create member as username already exists: {Username}", username);
|
||||
return null;
|
||||
}
|
||||
|
||||
// See if the email is unique
|
||||
if (MemberService.GetByEmail(email) != null && RequiresUniqueEmail)
|
||||
{
|
||||
status = MembershipCreateStatus.DuplicateEmail;
|
||||
Current.Logger.LogWarning("Cannot create member as a member with the same email address exists: {Email}", email);
|
||||
return null;
|
||||
}
|
||||
|
||||
string salt;
|
||||
var encodedPassword = PasswordSecurity.HashNewPassword(Membership.HashAlgorithmType, password, out salt);
|
||||
|
||||
var member = MemberService.CreateWithIdentity(
|
||||
username,
|
||||
email,
|
||||
PasswordSecurity.FormatPasswordForStorage(Membership.HashAlgorithmType, encodedPassword, salt),
|
||||
memberTypeAlias,
|
||||
isApproved);
|
||||
|
||||
member.LastLoginDate = DateTime.Now;
|
||||
member.LastPasswordChangeDate = DateTime.Now;
|
||||
|
||||
MemberService.Save(member);
|
||||
|
||||
status = MembershipCreateStatus.Success;
|
||||
return ConvertToMembershipUser(member);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a user from the membership data source.
|
||||
/// </summary>
|
||||
/// <param name="username">The name of the user to delete.</param>
|
||||
/// <param name="deleteAllRelatedData">
|
||||
/// TODO: This setting currently has no effect
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// true if the user was successfully deleted; otherwise, false.
|
||||
/// </returns>
|
||||
public override bool DeleteUser(string username, bool deleteAllRelatedData)
|
||||
{
|
||||
var member = MemberService.GetByUsername(username);
|
||||
if (member == null)
|
||||
return false;
|
||||
|
||||
MemberService.Delete(member);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of membership users where the e-mail address contains the specified e-mail address to match.
|
||||
/// </summary>
|
||||
/// <param name="emailToMatch">The e-mail address to search for.</param>
|
||||
/// <param name="pageIndex">The index of the page of results to return. pageIndex is zero-based.</param>
|
||||
/// <param name="pageSize">The size of the page of results to return.</param>
|
||||
/// <param name="totalRecords">The total number of matched users.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Web.Security.MembershipUserCollection"></see> collection that contains a page of pageSize<see cref="T:System.Web.Security.MembershipUser"></see> objects beginning at the page specified by pageIndex.
|
||||
/// </returns>
|
||||
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
|
||||
{
|
||||
long totalRecords2;
|
||||
var byEmail = MemberService.FindByEmail(emailToMatch, pageIndex, pageSize, out totalRecords2, StringPropertyMatchType.Wildcard).ToArray();
|
||||
totalRecords = Convert.ToInt32(totalRecords2);
|
||||
|
||||
var collection = new MembershipUserCollection();
|
||||
foreach (var m in byEmail)
|
||||
{
|
||||
collection.Add(ConvertToMembershipUser(m));
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of membership users where the user name contains the specified user name to match.
|
||||
/// </summary>
|
||||
/// <param name="usernameToMatch">The user name to search for.</param>
|
||||
/// <param name="pageIndex">The index of the page of results to return. pageIndex is zero-based.</param>
|
||||
/// <param name="pageSize">The size of the page of results to return.</param>
|
||||
/// <param name="totalRecords">The total number of matched users.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Web.Security.MembershipUserCollection"></see> collection that contains a page of pageSize<see cref="T:System.Web.Security.MembershipUser"></see> objects beginning at the page specified by pageIndex.
|
||||
/// </returns>
|
||||
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
|
||||
{
|
||||
long totalRecords2;
|
||||
var byEmail = MemberService.FindByUsername(usernameToMatch, pageIndex, pageSize, out totalRecords2, StringPropertyMatchType.Wildcard).ToArray();
|
||||
totalRecords = Convert.ToInt32(totalRecords2);
|
||||
|
||||
var collection = new MembershipUserCollection();
|
||||
foreach (var m in byEmail)
|
||||
{
|
||||
collection.Add(ConvertToMembershipUser(m));
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of all the users in the data source in pages of data.
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">The index of the page of results to return. pageIndex is zero-based.</param>
|
||||
/// <param name="pageSize">The size of the page of results to return.</param>
|
||||
/// <param name="totalRecords">The total number of matched users.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Web.Security.MembershipUserCollection"></see> collection that contains a page of pageSize<see cref="T:System.Web.Security.MembershipUser"></see> objects beginning at the page specified by pageIndex.
|
||||
/// </returns>
|
||||
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
|
||||
{
|
||||
var membersList = new MembershipUserCollection();
|
||||
|
||||
long totalRecords2;
|
||||
var pagedMembers = MemberService.GetAll(pageIndex, pageSize, out totalRecords2);
|
||||
totalRecords = Convert.ToInt32(totalRecords2);
|
||||
|
||||
foreach (var m in pagedMembers)
|
||||
{
|
||||
membersList.Add(ConvertToMembershipUser(m));
|
||||
}
|
||||
return membersList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of users currently accessing the application.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The number of users currently accessing the application.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// The way this is done is the same way that it is done in the MS SqlMembershipProvider - We query for any members
|
||||
/// that have their last active date within the Membership.UserIsOnlineTimeWindow (which is in minutes). It isn't exact science
|
||||
/// but that is how MS have made theirs so we'll follow that principal.
|
||||
/// </remarks>
|
||||
public override int GetNumberOfUsersOnline()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the password for the specified user name from the data source.
|
||||
/// </summary>
|
||||
/// <param name="username">The user to retrieve the password for.</param>
|
||||
/// <param name="answer">The password answer for the user.</param>
|
||||
/// <returns>
|
||||
/// The password for the specified user name.
|
||||
/// </returns>
|
||||
protected override string PerformGetPassword(string username, string answer)
|
||||
{
|
||||
var m = MemberService.GetByUsername(username);
|
||||
if (m == null)
|
||||
{
|
||||
throw new ProviderException("The supplied user is not found");
|
||||
}
|
||||
|
||||
var decodedPassword = DecryptPassword(m.RawPasswordValue);
|
||||
|
||||
return decodedPassword;
|
||||
}
|
||||
|
||||
internal string EncryptString(string str)
|
||||
{
|
||||
if (str.IsNullOrWhiteSpace())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
var bytes = Encoding.Unicode.GetBytes(str);
|
||||
var password = new byte[bytes.Length];
|
||||
Buffer.BlockCopy(bytes, 0, password, 0, bytes.Length);
|
||||
var encBytes = EncryptPassword(password, MembershipPasswordCompatibilityMode.Framework40);
|
||||
return Convert.ToBase64String(encBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information from the data source for a user. Provides an option to update the last-activity date/time stamp for the user.
|
||||
/// </summary>
|
||||
/// <param name="username">The name of the user to get information for.</param>
|
||||
/// <param name="userIsOnline">true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Web.Security.MembershipUser"></see> object populated with the specified user's information from the data source.
|
||||
/// </returns>
|
||||
public override MembershipUser GetUser(string username, bool userIsOnline)
|
||||
{
|
||||
var member = MemberService.GetByUsername(username);
|
||||
if (member == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (userIsOnline)
|
||||
{
|
||||
// when upgrading from 7.2 to 7.3 trying to save will throw
|
||||
if (_umbracoVersion.Version >= new Version(7, 3, 0, 0))
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
// update the database data directly instead of a full member save which requires DB locks
|
||||
MemberService.SetLastLogin(username, now);
|
||||
member.LastLoginDate = now;
|
||||
member.UpdateDate = now;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ConvertToMembershipUser(member);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information from the data source for a user based on the unique identifier for the membership user. Provides an option to update the last-activity date/time stamp for the user.
|
||||
/// </summary>
|
||||
/// <param name="providerUserKey">The unique identifier for the membership user to get information for.</param>
|
||||
/// <param name="userIsOnline">true to update the last-activity date/time stamp for the user; false to return user information without updating the last-activity date/time stamp for the user.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Web.Security.MembershipUser"></see> object populated with the specified user's information from the data source.
|
||||
/// </returns>
|
||||
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
|
||||
{
|
||||
var member = MemberService.GetByProviderKey(providerUserKey);
|
||||
if (member == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (userIsOnline)
|
||||
{
|
||||
member.LastLoginDate = DateTime.Now;
|
||||
member.UpdateDate = DateTime.Now;
|
||||
//don't raise events for this! It just sets the member dates, if we do raise events this will
|
||||
// cause all distributed cache to execute - which will clear out some caches we don't want.
|
||||
// http://issues.umbraco.org/issue/U4-3451
|
||||
MemberService.Save(member, false);
|
||||
}
|
||||
|
||||
return ConvertToMembershipUser(member);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user name associated with the specified e-mail address.
|
||||
/// </summary>
|
||||
/// <param name="email">The e-mail address to search for.</param>
|
||||
/// <returns>
|
||||
/// The user name associated with the specified e-mail address. If no match is found, return null.
|
||||
/// </returns>
|
||||
public override string GetUserNameByEmail(string email)
|
||||
{
|
||||
var member = MemberService.GetByEmail(email);
|
||||
|
||||
return member == null ? null : member.Username;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets a user's password to a new, automatically generated password.
|
||||
/// </summary>
|
||||
/// <param name="username">The user to reset the password for.</param>
|
||||
/// <param name="answer">The password answer for the specified user (not used with Umbraco).</param>
|
||||
/// <param name="generatedPassword"></param>
|
||||
/// <returns>The new password for the specified user.</returns>
|
||||
protected override string PerformResetPassword(string username, string answer, string generatedPassword)
|
||||
{
|
||||
// TODO: This should be here - but how do we update failure count in this provider??
|
||||
//if (answer == null && RequiresQuestionAndAnswer)
|
||||
//{
|
||||
// UpdateFailureCount(username, "passwordAnswer");
|
||||
// throw new ProviderException("Password answer required for password reset.");
|
||||
//}
|
||||
|
||||
var m = MemberService.GetByUsername(username);
|
||||
if (m == null)
|
||||
{
|
||||
throw new ProviderException("The supplied user is not found");
|
||||
}
|
||||
|
||||
if (m.IsLockedOut)
|
||||
{
|
||||
throw new ProviderException("The member is locked out.");
|
||||
}
|
||||
|
||||
string salt;
|
||||
var encodedPassword = PasswordSecurity.HashNewPassword(Membership.HashAlgorithmType, generatedPassword, out salt);
|
||||
m.RawPasswordValue = PasswordSecurity.FormatPasswordForStorage(Membership.HashAlgorithmType, encodedPassword, salt);
|
||||
m.LastPasswordChangeDate = DateTime.Now;
|
||||
MemberService.Save(m);
|
||||
|
||||
return generatedPassword;
|
||||
}
|
||||
|
||||
internal virtual bool PerformUnlockUser(string username, out TEntity member)
|
||||
{
|
||||
member = MemberService.GetByUsername(username);
|
||||
if (member == null)
|
||||
{
|
||||
throw new ProviderException(string.Format("No member with the username '{0}' found", username));
|
||||
}
|
||||
|
||||
// Non need to update
|
||||
if (member.IsLockedOut == false)
|
||||
return true;
|
||||
|
||||
member.IsLockedOut = false;
|
||||
member.FailedPasswordAttempts = 0;
|
||||
|
||||
MemberService.Save(member);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears a lock so that the membership user can be validated.
|
||||
/// </summary>
|
||||
/// <param name="username">The membership user to clear the lock status for.</param>
|
||||
/// <returns>
|
||||
/// true if the membership user was successfully unlocked; otherwise, false.
|
||||
/// </returns>
|
||||
public override bool UnlockUser(string username)
|
||||
{
|
||||
TEntity member;
|
||||
var result = PerformUnlockUser(username, out member);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates e-mail approved status, lock status and comment on a user.
|
||||
/// </summary>
|
||||
/// <param name="user">A <see cref="T:System.Web.Security.MembershipUser"></see> object that represents the user to update and the updated information for the user.</param>
|
||||
public override void UpdateUser(MembershipUser user)
|
||||
{
|
||||
var m = MemberService.GetByUsername(user.UserName);
|
||||
|
||||
if (m == null)
|
||||
{
|
||||
throw new ProviderException(string.Format("No member with the username '{0}' found", user.UserName));
|
||||
}
|
||||
|
||||
if (RequiresUniqueEmail && user.Email.Trim().IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
long totalRecs;
|
||||
var byEmail = MemberService.FindByEmail(user.Email.Trim(), 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Exact);
|
||||
if (byEmail.Count(x => x.Id != m.Id) > 0)
|
||||
{
|
||||
throw new ProviderException(string.Format("A member with the email '{0}' already exists", user.Email));
|
||||
}
|
||||
}
|
||||
m.Email = user.Email;
|
||||
|
||||
m.IsApproved = user.IsApproved;
|
||||
m.IsLockedOut = user.IsLockedOut;
|
||||
if (user.IsLockedOut)
|
||||
{
|
||||
m.LastLockoutDate = DateTime.Now;
|
||||
}
|
||||
m.Comments = user.Comment;
|
||||
|
||||
MemberService.Save(m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal virtual ValidateUserResult PerformValidateUser(string username, string password)
|
||||
{
|
||||
var member = MemberService.GetByUsername(username);
|
||||
|
||||
if (member == null)
|
||||
{
|
||||
Current.Logger.LogInformation("Login attempt failed for username {Username} from IP address {IpAddress}, the user does not exist", username, _ipResolver.GetCurrentRequestIpAddress());
|
||||
|
||||
return new ValidateUserResult
|
||||
{
|
||||
Authenticated = false
|
||||
};
|
||||
}
|
||||
|
||||
if (member.IsApproved == false)
|
||||
{
|
||||
Current.Logger.LogInformation("Login attempt failed for username {Username} from IP address {IpAddress}, the user is not approved", username, _ipResolver.GetCurrentRequestIpAddress());
|
||||
|
||||
return new ValidateUserResult
|
||||
{
|
||||
Member = member,
|
||||
Authenticated = false
|
||||
};
|
||||
}
|
||||
if (member.IsLockedOut)
|
||||
{
|
||||
Current.Logger.LogInformation("Login attempt failed for username {Username} from IP address {IpAddress}, the user is locked", username, _ipResolver.GetCurrentRequestIpAddress());
|
||||
|
||||
return new ValidateUserResult
|
||||
{
|
||||
Member = member,
|
||||
Authenticated = false
|
||||
};
|
||||
}
|
||||
|
||||
var authenticated = PasswordSecurity.VerifyPassword(Membership.HashAlgorithmType, password, member.RawPasswordValue);
|
||||
|
||||
var requiresFullSave = false;
|
||||
|
||||
if (authenticated == false)
|
||||
{
|
||||
// TODO: Increment login attempts - lock if too many.
|
||||
|
||||
var count = member.FailedPasswordAttempts;
|
||||
count++;
|
||||
member.FailedPasswordAttempts = count;
|
||||
|
||||
if (count >= MaxInvalidPasswordAttempts)
|
||||
{
|
||||
member.IsLockedOut = true;
|
||||
member.LastLockoutDate = DateTime.Now;
|
||||
|
||||
Current.Logger.LogInformation("Login attempt failed for username {Username} from IP address {IpAddress}, the user is now locked out, max invalid password attempts exceeded", username, _ipResolver.GetCurrentRequestIpAddress());
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.Logger.LogInformation("Login attempt failed for username {Username} from IP address {IpAddress}", username, _ipResolver.GetCurrentRequestIpAddress());
|
||||
}
|
||||
|
||||
requiresFullSave = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (member.FailedPasswordAttempts > 0)
|
||||
{
|
||||
//we have successfully logged in, reset the AccessFailedCount
|
||||
member.FailedPasswordAttempts = 0;
|
||||
requiresFullSave = true;
|
||||
}
|
||||
|
||||
member.LastLoginDate = DateTime.Now;
|
||||
|
||||
Current.Logger.LogInformation("Login attempt succeeded for username {Username} from IP address {IpAddress}", username, _ipResolver.GetCurrentRequestIpAddress());
|
||||
}
|
||||
|
||||
// don't raise events for this! It just sets the member dates, if we do raise events this will
|
||||
// cause all distributed cache to execute - which will clear out some caches we don't want.
|
||||
// http://issues.umbraco.org/issue/U4-3451
|
||||
// TODO: In v8 we aren't going to have an overload to disable events, so we'll need to make a different method
|
||||
// for this type of thing (i.e. UpdateLastLogin or similar).
|
||||
|
||||
if (requiresFullSave)
|
||||
{
|
||||
// when upgrading from 7.2 to 7.3 trying to save will throw
|
||||
if (_umbracoVersion.Version >= new Version(7, 3, 0, 0))
|
||||
{
|
||||
// We need to raise event to ensure caches are updated. (e.g. the cache that uses username as key).
|
||||
// Even that this is a heavy operation, because indexes are updates, we consider that okay, as it
|
||||
// is still cheap to do a successful login.
|
||||
MemberService.Save(member, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// set the last login date without full save (fast, no locks).
|
||||
// We do not update caches. This is to the best of our knowledge okay, as this info are only stored
|
||||
// because it is required by the membership provider.
|
||||
// If we one day have to revisit this, we will most likely need to spilt the events in membership info
|
||||
// saved and umbraco info saved. We don't want to update indexes etc when it is just membership info that is saved
|
||||
MemberService.SetLastLogin(member.Username, member.LastLoginDate);
|
||||
}
|
||||
|
||||
return new ValidateUserResult
|
||||
{
|
||||
Authenticated = authenticated,
|
||||
Member = member
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the specified user name and password exist in the data source.
|
||||
/// </summary>
|
||||
/// <param name="username">The name of the user to validate.</param>
|
||||
/// <param name="password">The password for the specified user.</param>
|
||||
/// <returns>
|
||||
/// true if the specified username and password are valid; otherwise, false.
|
||||
/// </returns>
|
||||
public override bool ValidateUser(string username, string password)
|
||||
{
|
||||
var result = PerformValidateUser(username, password);
|
||||
return result.Authenticated;
|
||||
}
|
||||
|
||||
internal class ValidateUserResult
|
||||
{
|
||||
public TEntity Member { get; set; }
|
||||
public bool Authenticated { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
using System.Text;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
|
||||
namespace Umbraco.Web.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// A base membership provider class for umbraco providers
|
||||
/// </summary>
|
||||
public abstract class UmbracoMembershipProviderBase : MembershipProviderBase
|
||||
{
|
||||
protected UmbracoMembershipProviderBase(IHostingEnvironment hostingEnvironment) : base(hostingEnvironment)
|
||||
{
|
||||
}
|
||||
|
||||
public abstract LegacyPasswordSecurity PasswordSecurity { get; }
|
||||
public abstract string DefaultMemberTypeAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new membership user to the data source.
|
||||
/// </summary>
|
||||
/// <param name="username">The user name for the new user.</param>
|
||||
/// <param name="password">The password for the new user.</param>
|
||||
/// <param name="email">The e-mail address for the new user.</param>
|
||||
/// <param name="passwordQuestion">The password question for the new user.</param>
|
||||
/// <param name="passwordAnswer">The password answer for the new user</param>
|
||||
/// <param name="isApproved">Whether or not the new user is approved to be validated.</param>
|
||||
/// <param name="providerUserKey">The unique identifier from the membership data source for the user.</param>
|
||||
/// <param name="status">A <see cref="T:System.Web.Security.MembershipCreateStatus"></see> enumeration value indicating whether the user was created successfully.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Web.Security.MembershipUser"></see> object populated with the information for the newly created user.
|
||||
/// </returns>
|
||||
protected sealed override MembershipUser PerformCreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
|
||||
{
|
||||
return PerformCreateUser(DefaultMemberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new membership user to the data source.
|
||||
/// </summary>
|
||||
/// <param name="memberTypeAlias">The member type alias to use when creating the member</param>
|
||||
/// <param name="username">The user name for the new user.</param>
|
||||
/// <param name="password">The password for the new user.</param>
|
||||
/// <param name="email">The e-mail address for the new user.</param>
|
||||
/// <param name="passwordQuestion">The password question for the new user.</param>
|
||||
/// <param name="passwordAnswer">The password answer for the new user</param>
|
||||
/// <param name="isApproved">Whether or not the new user is approved to be validated.</param>
|
||||
/// <param name="providerUserKey">The unique identifier from the membership data source for the user.</param>
|
||||
/// <param name="status">A <see cref="T:System.Web.Security.MembershipCreateStatus"></see> enumeration value indicating whether the user was created successfully.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Web.Security.MembershipUser"></see> object populated with the information for the newly created user.
|
||||
/// </returns>
|
||||
public MembershipUser CreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
|
||||
{
|
||||
//do the base validation first
|
||||
var valStatus = ValidateNewUser(username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey);
|
||||
if (valStatus != MembershipCreateStatus.Success)
|
||||
{
|
||||
status = valStatus;
|
||||
return null;
|
||||
}
|
||||
|
||||
return PerformCreateUser(memberTypeAlias, username, password, email, passwordQuestion, passwordAnswer, isApproved, providerUserKey, out status);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new membership user to the data source.
|
||||
/// </summary>
|
||||
/// <param name="memberTypeAlias">The member type alias to use when creating the member</param>
|
||||
/// <param name="username">The user name for the new user.</param>
|
||||
/// <param name="password">The password for the new user.</param>
|
||||
/// <param name="email">The e-mail address for the new user.</param>
|
||||
/// <param name="passwordQuestion">The password question for the new user.</param>
|
||||
/// <param name="passwordAnswer">The password answer for the new user</param>
|
||||
/// <param name="isApproved">Whether or not the new user is approved to be validated.</param>
|
||||
/// <param name="providerUserKey">The unique identifier from the membership data source for the user.</param>
|
||||
/// <param name="status">A <see cref="T:System.Web.Security.MembershipCreateStatus"></see> enumeration value indicating whether the user was created successfully.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="T:System.Web.Security.MembershipUser"></see> object populated with the information for the newly created user.
|
||||
/// </returns>
|
||||
protected abstract MembershipUser PerformCreateUser(string memberTypeAlias, string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var result = base.ToString();
|
||||
var sb = new StringBuilder(result);
|
||||
sb.AppendLine("DefaultMemberTypeAlias=" + DefaultMemberTypeAlias);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,16 +139,12 @@
|
||||
<Compile Include="HttpContextExtensions.cs" />
|
||||
<Compile Include="Security\BackOfficeSecurity.cs" />
|
||||
<Compile Include="HttpContextAccessorExtensions.cs" />
|
||||
<Compile Include="Models\Membership\UmbracoMembershipMember.cs" />
|
||||
<Compile Include="AspNet\AspNetBackOfficeInfo.cs" />
|
||||
<Compile Include="AspNet\AspNetCookieManager.cs" />
|
||||
<Compile Include="AspNet\AspNetHttpContextAccessor.cs" />
|
||||
<Compile Include="AspNet\AspNetIpResolver.cs" />
|
||||
<Compile Include="AspNet\AspNetPasswordHasher.cs" />
|
||||
<Compile Include="Runtime\AspNetUmbracoBootPermissionChecker.cs" />
|
||||
<Compile Include="Security\MembershipProviderBase.cs" />
|
||||
<Compile Include="Security\MembershipProviderExtensions.cs" />
|
||||
<Compile Include="Security\UmbracoMembershipProviderBase.cs" />
|
||||
<Compile Include="StringExtensions.cs" />
|
||||
<Compile Include="UmbracoContext.cs" />
|
||||
<Compile Include="UmbracoContextFactory.cs" />
|
||||
@@ -172,10 +168,7 @@
|
||||
<Compile Include="Mvc\UmbracoVirtualNodeRouteHandler.cs" />
|
||||
<Compile Include="Security\AuthenticationOptionsExtensions.cs" />
|
||||
<Compile Include="Mvc\ViewDataDictionaryExtensions.cs" />
|
||||
<Compile Include="Security\MembershipHelper.cs" />
|
||||
<Compile Include="HttpCookieExtensions.cs" />
|
||||
<Compile Include="Security\Providers\MembersMembershipProvider.cs" />
|
||||
<Compile Include="Security\Providers\UmbracoMembershipProvider.cs" />
|
||||
<Compile Include="HttpRequestExtensions.cs" />
|
||||
<Compile Include="UrlHelperExtensions.cs" />
|
||||
<Compile Include="UrlHelperRenderExtensions.cs" />
|
||||
@@ -250,4 +243,4 @@
|
||||
<!-- <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />-->
|
||||
<!-- </SGen>-->
|
||||
<!-- </Target>-->
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -27,7 +27,6 @@ namespace Umbraco.Web
|
||||
{
|
||||
private readonly IPublishedContentQuery _publishedContentQuery;
|
||||
private readonly IUmbracoComponentRenderer _componentRenderer;
|
||||
private readonly MembershipHelper _membershipHelper;
|
||||
private readonly ICultureDictionaryFactory _cultureDictionaryFactory;
|
||||
|
||||
private IPublishedContent _currentPage;
|
||||
@@ -47,12 +46,10 @@ namespace Umbraco.Web
|
||||
public UmbracoHelper(IPublishedContent currentPage,
|
||||
ICultureDictionaryFactory cultureDictionary,
|
||||
IUmbracoComponentRenderer componentRenderer,
|
||||
IPublishedContentQuery publishedContentQuery,
|
||||
MembershipHelper membershipHelper)
|
||||
IPublishedContentQuery publishedContentQuery)
|
||||
{
|
||||
_cultureDictionaryFactory = cultureDictionary ?? throw new ArgumentNullException(nameof(cultureDictionary));
|
||||
_componentRenderer = componentRenderer ?? throw new ArgumentNullException(nameof(componentRenderer));
|
||||
_membershipHelper = membershipHelper ?? throw new ArgumentNullException(nameof(membershipHelper));
|
||||
_publishedContentQuery = publishedContentQuery ?? throw new ArgumentNullException(nameof(publishedContentQuery));
|
||||
_currentPage = currentPage;
|
||||
}
|
||||
@@ -205,71 +202,6 @@ namespace Umbraco.Web
|
||||
|
||||
#endregion
|
||||
|
||||
#region Members
|
||||
|
||||
public IPublishedContent Member(Udi id)
|
||||
{
|
||||
var guidUdi = id as GuidUdi;
|
||||
return guidUdi == null ? null : Member(guidUdi.Guid);
|
||||
}
|
||||
|
||||
public IPublishedContent Member(Guid id)
|
||||
{
|
||||
return _membershipHelper.GetById(id);
|
||||
}
|
||||
|
||||
public IPublishedContent Member(int id)
|
||||
{
|
||||
return _membershipHelper.GetById(id);
|
||||
}
|
||||
|
||||
public IPublishedContent Member(string id)
|
||||
{
|
||||
var asInt = id.TryConvertTo<int>();
|
||||
return asInt ? _membershipHelper.GetById(asInt.Result) : _membershipHelper.GetByProviderKey(id);
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(IEnumerable<int> ids)
|
||||
{
|
||||
return _membershipHelper.GetByIds(ids);
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(IEnumerable<string> ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(IEnumerable<Guid> ids)
|
||||
{
|
||||
return _membershipHelper.GetByIds(ids);
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(IEnumerable<Udi> ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
public IEnumerable<IPublishedContent> Members(params int[] ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(params string[] ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(params Guid[] ids)
|
||||
{
|
||||
return _membershipHelper.GetByIds(ids);
|
||||
}
|
||||
|
||||
public IEnumerable<IPublishedContent> Members(params Udi[] ids)
|
||||
{
|
||||
return ids.Select(Member).WhereNotNull();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user