Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/core-cannot-use-system-web

# Conflicts:
#	src/Umbraco.Core/Runtime/CoreInitialComposer.cs
#	src/Umbraco.Core/StringExtensions.cs
#	src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs
#	src/Umbraco.Tests/Membership/UmbracoServiceMembershipProviderTests.cs
#	src/Umbraco.Tests/Services/MemberServiceTests.cs
#	src/Umbraco.Web/Security/AppBuilderExtensions.cs
#	src/Umbraco.Web/Security/BackOfficeUserManager.cs
#	src/Umbraco.Web/Security/MembershipProviderBase.cs
#	src/Umbraco.Web/Security/MembershipProviderExtensions.cs
#	src/Umbraco.Web/Security/Providers/UsersMembershipProvider.cs
#	src/Umbraco.Web/Security/UmbracoMembershipProviderBase.cs
#	src/Umbraco.Web/UmbracoDefaultOwinStartup.cs
This commit is contained in:
Bjarke Berg
2019-11-26 11:51:50 +01:00
135 changed files with 1500 additions and 4035 deletions
@@ -25,6 +25,9 @@ namespace Umbraco.Core
public static IUmbracoSettingsSection Settings(this Configs configs)
=> configs.GetConfig<IUmbracoSettingsSection>();
public static IUserPasswordConfiguration UserPasswordConfig(this Configs configs)
=> configs.GetConfig<IUserPasswordConfiguration>();
public static IHealthChecks HealthChecks(this Configs configs)
=> configs.GetConfig<IHealthChecks>();
@@ -0,0 +1,21 @@
namespace Umbraco.Core.Configuration
{
/// <summary>
/// Password configuration
/// </summary>
public interface IPasswordConfiguration
{
int RequiredLength { get; }
bool RequireNonLetterOrDigit { get; }
bool RequireDigit { get; }
bool RequireLowercase { get; }
bool RequireUppercase { get; }
bool UseLegacyEncoding { get; }
string HashAlgorithmType { get; }
// TODO: This doesn't really belong here
int MaxFailedAccessAttemptsBeforeLockout { get; }
}
}
@@ -0,0 +1,10 @@
namespace Umbraco.Core.Configuration
{
/// <summary>
/// The password configuration for back office users
/// </summary>
public interface IUserPasswordConfiguration : IPasswordConfiguration
{
}
}
@@ -137,21 +137,7 @@ namespace Umbraco.Core
public static readonly string UmbracoMemberProviderName = "UmbracoMembershipProvider";
public static readonly string UmbracoRoleProviderName = "UmbracoRoleProvider";
/// <summary>
/// Property alias for a Members Password Question
/// </summary>
public const string PasswordQuestion = "umbracoMemberPasswordRetrievalQuestion";
public const string PasswordQuestionLabel = "Password Question";
/// <summary>
/// Property alias for Members Password Answer
/// </summary>
public const string PasswordAnswer = "umbracoMemberPasswordRetrievalAnswer";
public const string PasswordAnswerLabel = "Password Answer";
/// <summary>
/// Property alias for the Comments on a Member
/// </summary>
@@ -4,8 +4,8 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
namespace Umbraco.Core
{
@@ -251,7 +251,7 @@ namespace Umbraco.Core
var builder = new StringBuilder();
foreach (var i in d)
{
builder.Append(String.Format("{0}={1}&", HttpUtility.UrlEncode(i.Key), i.Value == null ? string.Empty : HttpUtility.UrlEncode(i.Value.ToString())));
builder.Append(String.Format("{0}={1}&", WebUtility.UrlEncode(i.Key), i.Value == null ? string.Empty : WebUtility.UrlEncode(i.Value.ToString())));
}
return builder.ToString().TrimEnd('&');
}
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Models
/// <para>A set of cultures can be either all cultures (including the invariant culture), or
/// the invariant culture, or a specific culture.</para>
/// </remarks>
internal class CultureImpact
public sealed class CultureImpact
{
/// <summary>
/// Utility method to return the culture used for invariant property errors based on what cultures are being actively saved,
@@ -8,7 +8,6 @@ namespace Umbraco.Core.Models.Membership
/// </summary>
public interface IMembershipUser : IEntity
{
object ProviderUserKey { get; set; }
string Username { get; set; }
string Email { get; set; }
@@ -17,13 +16,6 @@ namespace Umbraco.Core.Models.Membership
/// </summary>
string RawPasswordValue { get; set; }
string PasswordQuestion { get; set; }
/// <summary>
/// Gets or sets the raw password answer value
/// </summary>
string RawPasswordAnswerValue { get; set; }
string Comments { get; set; }
bool IsApproved { get; set; }
bool IsLockedOut { get; set; }
@@ -6,7 +6,6 @@
public enum MemberCountType
{
All,
Online,
LockedOut,
Approved
}
@@ -1,35 +0,0 @@
namespace Umbraco.Core.Models.Membership
{
/// <summary>
/// How membership is implemented in the current install.
/// </summary>
public enum MembershipScenario
{
//TODO: This will become obsolete when we get asp.net identity members in place
/// <summary>
/// The member is based on the native Umbraco members (IMember + Umbraco membership provider)
/// </summary>
/// <remarks>
/// This supports custom member properties
/// </remarks>
NativeUmbraco,
/// <summary>
/// The member is based on a custom member provider but it is linked to an IMember
/// </summary>
/// <remarks>
/// This supports custom member properties (but that is not enabled yet)
/// </remarks>
CustomProviderWithUmbracoLink,
/// <summary>
/// The member is based purely on a custom member provider and is not linked to umbraco data
/// </summary>
/// <remarks>
/// This does not support custom member properties.
/// </remarks>
StandaloneCustomProvider
}
}
@@ -102,16 +102,15 @@ namespace Umbraco.Core.Models.PublishedContent
// TODO: this list somehow also exists in constants, see memberTypeRepository => remove duplicate!
private static readonly Dictionary<string, int> BuiltinMemberProperties = new Dictionary<string, int>
{
{ "Email", Constants.DataTypes.Textbox },
{ "Username", Constants.DataTypes.Textbox },
{ "PasswordQuestion", Constants.DataTypes.Textbox },
{ "Comments", Constants.DataTypes.Textbox },
{ "IsApproved", Constants.DataTypes.Boolean },
{ "IsLockedOut", Constants.DataTypes.Boolean },
{ "LastLockoutDate", Constants.DataTypes.DateTime },
{ "CreateDate", Constants.DataTypes.DateTime },
{ "LastLoginDate", Constants.DataTypes.DateTime },
{ "LastPasswordChangeDate", Constants.DataTypes.DateTime },
{ nameof(IMember.Email), Constants.DataTypes.Textbox },
{ nameof(IMember.Username), Constants.DataTypes.Textbox },
{ nameof(IMember.Comments), Constants.DataTypes.Textbox },
{ nameof(IMember.IsApproved), Constants.DataTypes.Boolean },
{ nameof(IMember.IsLockedOut), Constants.DataTypes.Boolean },
{ nameof(IMember.LastLockoutDate), Constants.DataTypes.DateTime },
{ nameof(IMember.CreateDate), Constants.DataTypes.DateTime },
{ nameof(IMember.LastLoginDate), Constants.DataTypes.DateTime },
{ nameof(IMember.LastPasswordChangeDate), Constants.DataTypes.DateTime },
};
#region Content type
@@ -0,0 +1,17 @@
using Umbraco.Core.Configuration;
namespace Umbraco.Core.Security
{
/// <summary>
/// Generates a password
/// </summary>
public interface IPasswordGenerator
{
/// <summary>
/// Generates a password
/// </summary>
/// <param name="passwordConfiguration"></param>
/// <returns></returns>
string GeneratePassword(IPasswordConfiguration passwordConfiguration);
}
}
@@ -0,0 +1,154 @@
using System;
using System.Linq;
using System.Security.Cryptography;
using Umbraco.Core.Configuration;
namespace Umbraco.Core.Security
{
/// <summary>
/// Generates a password
/// </summary>
/// <remarks>
/// This uses logic copied from the old MembershipProvider.GeneratePassword logic
/// </remarks>
public class PasswordGenerator : IPasswordGenerator
{
public string GeneratePassword(IPasswordConfiguration passwordConfiguration)
{
var password = PasswordStore.GeneratePassword(
passwordConfiguration.RequiredLength,
passwordConfiguration.RequireNonLetterOrDigit ? 2 : 0);
var random = new Random();
var passwordChars = password.ToCharArray();
if (passwordConfiguration.RequireDigit && passwordChars.ContainsAny(Enumerable.Range(48, 58).Select(x => (char)x)))
password += Convert.ToChar(random.Next(48, 58)); // 0-9
if (passwordConfiguration.RequireLowercase && passwordChars.ContainsAny(Enumerable.Range(97, 123).Select(x => (char)x)))
password += Convert.ToChar(random.Next(97, 123)); // a-z
if (passwordConfiguration.RequireUppercase && passwordChars.ContainsAny(Enumerable.Range(65, 91).Select(x => (char)x)))
password += Convert.ToChar(random.Next(65, 91)); // A-Z
if (passwordConfiguration.RequireNonLetterOrDigit && passwordChars.ContainsAny(Enumerable.Range(33, 48).Select(x => (char)x)))
password += Convert.ToChar(random.Next(33, 48)); // symbols !"#$%&'()*+,-./
return password;
}
/// <summary>
/// Internal class copied from ASP.NET Framework MembershipProvider
/// </summary>
/// <remarks>
/// See https://stackoverflow.com/a/39855417/694494 + https://github.com/Microsoft/referencesource/blob/master/System.Web/Security/Membership.cs
/// </remarks>
private static class PasswordStore
{
private static readonly char[] Punctuations = "!@#$%^&*()_-+=[{]};:>|./?".ToCharArray();
private static readonly char[] StartingChars = new char[] { '<', '&' };
/// <summary>Generates a random password of the specified length.</summary>
/// <returns>A random password of the specified length.</returns>
/// <param name="length">The number of characters in the generated password. The length must be between 1 and 128 characters. </param>
/// <param name="numberOfNonAlphanumericCharacters">The minimum number of non-alphanumeric characters (such as @, #, !, %, &amp;, and so on) in the generated password.</param>
/// <exception cref="T:System.ArgumentException">
/// <paramref name="length" /> is less than 1 or greater than 128 -or-<paramref name="numberOfNonAlphanumericCharacters" /> is less than 0 or greater than <paramref name="length" />. </exception>
public static string GeneratePassword(int length, int numberOfNonAlphanumericCharacters)
{
if (length < 1 || length > 128)
throw new ArgumentException("password length incorrect", nameof(length));
if (numberOfNonAlphanumericCharacters > length || numberOfNonAlphanumericCharacters < 0)
throw new ArgumentException("min required non alphanumeric characters incorrect", nameof(numberOfNonAlphanumericCharacters));
string s;
int matchIndex;
do
{
var data = new byte[length];
var chArray = new char[length];
var num1 = 0;
new RNGCryptoServiceProvider().GetBytes(data);
for (var index = 0; index < length; ++index)
{
var num2 = (int)data[index] % 87;
if (num2 < 10)
chArray[index] = (char)(48 + num2);
else if (num2 < 36)
chArray[index] = (char)(65 + num2 - 10);
else if (num2 < 62)
{
chArray[index] = (char)(97 + num2 - 36);
}
else
{
chArray[index] = Punctuations[num2 - 62];
++num1;
}
}
if (num1 < numberOfNonAlphanumericCharacters)
{
var random = new Random();
for (var index1 = 0; index1 < numberOfNonAlphanumericCharacters - num1; ++index1)
{
int index2;
do
{
index2 = random.Next(0, length);
}
while (!char.IsLetterOrDigit(chArray[index2]));
chArray[index2] = Punctuations[random.Next(0, Punctuations.Length)];
}
}
s = new string(chArray);
}
while (IsDangerousString(s, out matchIndex));
return s;
}
private static bool IsDangerousString(string s, out int matchIndex)
{
//bool inComment = false;
matchIndex = 0;
for (var i = 0; ;)
{
// Look for the start of one of our patterns
var n = s.IndexOfAny(StartingChars, i);
// If not found, the string is safe
if (n < 0) return false;
// If it's the last char, it's safe
if (n == s.Length - 1) return false;
matchIndex = n;
switch (s[n])
{
case '<':
// If the < is followed by a letter or '!', it's unsafe (looks like a tag or HTML comment)
if (IsAtoZ(s[n + 1]) || s[n + 1] == '!' || s[n + 1] == '/' || s[n + 1] == '?') return true;
break;
case '&':
// If the & is followed by a #, it's unsafe (e.g. &#83;)
if (s[n + 1] == '#') return true;
break;
}
// Continue searching
i = n + 1;
}
}
private static bool IsAtoZ(char c)
{
if ((int)c >= 97 && (int)c <= 122)
return true;
if ((int)c >= 65)
return (int)c <= 90;
return false;
}
}
}
}
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Services.Changes
public TItem Item { get; }
public ContentTypeChangeTypes ChangeTypes { get; internal set; }
public ContentTypeChangeTypes ChangeTypes { get; set; }
public EventArgs ToEventArgs(ContentTypeChange<TItem> change)
{
@@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Http;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Services
@@ -95,20 +91,6 @@ namespace Umbraco.Core.Services
/// <returns><see cref="IMember"/></returns>
IMember CreateMemberWithIdentity(string username, string email, string name, IMemberType memberType);
/// <summary>
/// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method which can be
/// used during Member creation.
/// </summary>
/// <remarks>This method exists so that Umbraco developers can use one entry point to create/update
/// this will not work for updating members in most cases (depends on your membership provider settings)
///
/// It is preferred to use the membership APIs for working with passwords, in the near future this method will be obsoleted
/// and the ASP.NET Identity APIs should be used instead.
/// </remarks>
/// <param name="member">The Member to save the password for</param>
/// <param name="password">The password to encrypt and save</param>
void SavePassword(IMember member, string password);
/// <summary>
/// Gets the count of Members by an optional MemberType alias
/// </summary>
@@ -1,11 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Services
{
/// <summary>
/// Defines part of the MemberService, which is specific to methods used by the membership provider.
/// </summary>
/// <remarks>
/// Idea is to have this as an isolated interface so that it can be easily 'replaced' in the membership provider implementation.
/// </remarks>
public interface IMembershipMemberService : IMembershipMemberService<IMember>, IMembershipRoleService<IMember>
{
/// <summary>
/// Creates and persists a new Member
/// </summary>
/// <param name="username">Username of the Member to create</param>
/// <param name="email">Email of the Member to create</param>
/// <param name="memberType"><see cref="IMemberType"/> which the Member should be based on</param>
/// <returns><see cref="IMember"/></returns>
IMember CreateMemberWithIdentity(string username, string email, IMemberType memberType);
}
/// <summary>
/// Defines part of the UserService/MemberService, which is specific to methods used by the membership provider.
/// The generic type is restricted to <see cref="IMembershipUser"/>. The implementation of this interface uses
@@ -0,0 +1,17 @@
using Umbraco.Core.Models;
namespace Umbraco.Core.Services
{
public interface IPropertyValidationService
{
/// <summary>
/// Validates the content item's properties pass validation rules
/// </summary>
bool IsPropertyDataValid(IContent content, out IProperty[] invalidProperties, CultureImpact impact);
/// <summary>
/// Gets a value indicating whether the property has valid values.
/// </summary>
bool IsPropertyValid(IProperty property, string culture = "*", string segment = "*");
}
}
@@ -69,24 +69,7 @@ namespace Umbraco.Core.Services
.ToDictionary(keyvals => keyvals.index, keyvals => keyvals.value);
}
private static ICultureDictionary _cultureDictionary;
/// <summary>
/// TODO: We need to refactor how we work with ICultureDictionary - this is supposed to be the 'fast' way to
/// do readonly access to the Dictionary without using the ILocalizationService. See TODO Notes in `DefaultCultureDictionary`
/// Also NOTE that the ICultureDictionary is based on the ILocalizationService not the ILocalizedTextService (which is used
/// only for the localization files - not the dictionary)
/// </summary>
/// <param name="manager"></param>
/// <param name="text"></param>
/// <returns></returns>
internal static string UmbracoDictionaryTranslate(this ILocalizedTextService manager, string text)
{
var cultureDictionary = CultureDictionary;
return manager.UmbracoDictionaryTranslate(text, cultureDictionary);
}
private static string UmbracoDictionaryTranslate(this ILocalizedTextService manager, string text, ICultureDictionary cultureDictionary)
public static string UmbracoDictionaryTranslate(this ILocalizedTextService manager, ICultureDictionary cultureDictionary, string text)
{
if (text == null)
return null;
@@ -105,7 +88,5 @@ namespace Umbraco.Core.Services
return value.StartsWith("[") ? text : value;
}
private static ICultureDictionary CultureDictionary
=> _cultureDictionary ?? (_cultureDictionary = Current.CultureDictionaryFactory.CreateDictionary());
}
}
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Security;
using Umbraco.Core.Models;
namespace Umbraco.Core.Services
@@ -53,11 +52,6 @@ namespace Umbraco.Core.Services
return HasAccess(entry, username, currentMemberRoles);
}
public static bool HasAccess(this IPublicAccessService publicAccessService, string path, MembershipUser member, RoleProvider roleProvider)
{
return publicAccessService.HasAccess(path, member.UserName, roleProvider.GetRolesForUser);
}
/// <summary>
/// Checks if the member with the specified username has access to the path which is also based on the passed in roles for the member
/// </summary>
@@ -1,6 +1,5 @@
using System;
using System.Linq;
using System.Web.Security;
using Umbraco.Core.Models.Membership;
namespace Umbraco.Core.Services
@@ -69,40 +68,5 @@ namespace Umbraco.Core.Services
userService.ReplaceUserGroupPermissions(groupId, new char[] { });
}
/// <summary>
/// Maps a custom provider's information to an umbraco user account
/// </summary>
/// <param name="userService"></param>
/// <param name="member"></param>
/// <remarks>
/// To maintain compatibility we have to check the login name if the provider key lookup fails but otherwise
/// we'll store the provider user key in the login column.
/// </remarks>
internal static IUser CreateUserMappingForCustomProvider(this IUserService userService, MembershipUser member)
{
if (member == null) throw new ArgumentNullException("member");
var valToLookup = member.ProviderUserKey == null ? member.UserName : member.ProviderUserKey.ToString();
var found = userService.GetByUsername(valToLookup);
if (found == null && member.ProviderUserKey != null)
{
//try by username
found = userService.GetByUsername(member.UserName);
}
if (found == null)
{
var user = new User(
member.UserName,
member.Email ?? Guid.NewGuid().ToString("N") + "@example.com", //email cannot be empty
member.ProviderUserKey == null ? member.UserName : member.ProviderUserKey.ToString(),
Guid.NewGuid().ToString("N")); //pass cannot be empty
userService.Save(user);
return user;
}
return found;
}
}
}
@@ -25,10 +25,26 @@ namespace Umbraco.Core.Configuration
configs.Add<IUmbracoSettingsSection>("umbracoConfiguration/settings");
configs.Add<IHealthChecks>("umbracoConfiguration/HealthChecks");
configs.Add<IUserPasswordConfiguration>(() => new DefaultUserPasswordConfig());
configs.Add<ICoreDebug>(() => new CoreDebug());
configs.Add<IConnectionStrings>(() => new ConnectionStrings());
configs.AddCoreConfigs(_ioHelper);
return configs;
}
}
// Default/static user password configs
// TODO: Make this configurable somewhere - we've removed membership providers, so could be a section in the umbracosettings.config file?
// keeping in mind that we will also be removing the members membership provider so there will be 2x the same/similar configuration
internal class DefaultUserPasswordConfig : IUserPasswordConfiguration
{
public int RequiredLength => 12;
public bool RequireNonLetterOrDigit => false;
public bool RequireDigit => false;
public bool RequireLowercase => false;
public bool RequireUppercase => false;
public bool UseLegacyEncoding => false;
public string HashAlgorithmType => "HMACSHA256";
public int MaxFailedAccessAttemptsBeforeLockout => 5;
}
}
@@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
@@ -27,6 +28,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
composition.RegisterUnique<IdkMap>();
// register the services
composition.RegisterUnique<IPropertyValidationService, PropertyValidationService>();
composition.RegisterUnique<IKeyValueService, KeyValueService>();
composition.RegisterUnique<IPublicAccessService, PublicAccessService>();
composition.RegisterUnique<IDomainService, DomainService>();
+4
View File
@@ -12,6 +12,7 @@ using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Scoping;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
@@ -127,6 +128,9 @@ namespace Umbraco.Core.Composing
public static IRuntimeState RuntimeState
=> Factory.GetInstance<IRuntimeState>();
public static IPasswordGenerator PasswordGenerator
=> Factory.GetInstance<IPasswordGenerator>();
public static TypeLoader TypeLoader
=> Factory.GetInstance<TypeLoader>();
+1 -1
View File
@@ -91,7 +91,7 @@ namespace Umbraco.Core
public static void SetCultureDictionaryFactory<T>(this Composition composition)
where T : ICultureDictionaryFactory
{
composition.RegisterUnique<ICultureDictionaryFactory, T>();
composition.RegisterUnique<ICultureDictionaryFactory, T>();
}
/// <summary>
-18
View File
@@ -67,24 +67,6 @@ namespace Umbraco.Core
Name = Constants.Conventions.Member.LastPasswordChangeDateLabel,
DataTypeId = Constants.DataTypes.LabelDateTime
}
},
{
Constants.Conventions.Member.PasswordAnswer,
new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true,
Constants.Conventions.Member.PasswordAnswer)
{
Name = Constants.Conventions.Member.PasswordAnswerLabel,
DataTypeId = Constants.DataTypes.LabelString
}
},
{
Constants.Conventions.Member.PasswordQuestion,
new PropertyType(Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true,
Constants.Conventions.Member.PasswordQuestion)
{
Name = Constants.Conventions.Member.PasswordQuestionLabel,
DataTypeId = Constants.DataTypes.LabelString
}
}
};
}
@@ -5,9 +5,8 @@ using System.Linq;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
namespace Umbraco.Web.Dictionary
namespace Umbraco.Core.Dictionary
{
/// <summary>
@@ -18,28 +17,29 @@ namespace Umbraco.Web.Dictionary
/// The ILocalizationService is the service used for interacting with this data from the database which isn't all that fast
/// (even though there is caching involved, if there's lots of dictionary items the caching is not great)
/// </remarks>
public class DefaultCultureDictionary : Core.Dictionary.ICultureDictionary
public class DefaultCultureDictionary : ICultureDictionary
{
private readonly ILocalizationService _localizationService;
private readonly IAppCache _requestCache;
private readonly CultureInfo _specificCulture;
public DefaultCultureDictionary()
: this(Current.Services.LocalizationService, Current.AppCaches.RequestCache)
{ }
/// <summary>
/// Default constructor which will use the current thread's culture
/// </summary>
/// <param name="localizationService"></param>
/// <param name="requestCache"></param>
public DefaultCultureDictionary(ILocalizationService localizationService, IAppCache requestCache)
{
_localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService));
_requestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache));
}
public DefaultCultureDictionary(CultureInfo specificCulture)
: this(Current.Services.LocalizationService, Current.AppCaches.RequestCache)
{
_specificCulture = specificCulture ?? throw new ArgumentNullException(nameof(specificCulture));
}
/// <summary>
/// Constructor for testing to specify a static culture
/// </summary>
/// <param name="specificCulture"></param>
/// <param name="localizationService"></param>
/// <param name="requestCache"></param>
public DefaultCultureDictionary(CultureInfo specificCulture, ILocalizationService localizationService, IAppCache requestCache)
{
_localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService));
@@ -0,0 +1,28 @@
using Umbraco.Core.Cache;
using Umbraco.Core.Services;
namespace Umbraco.Core.Dictionary
{
/// <summary>
/// A culture dictionary factory used to create an Umbraco.Core.Dictionary.ICultureDictionary.
/// </summary>
/// <remarks>
/// In the future this will allow use to potentially store dictionary items elsewhere and allows for maximum flexibility.
/// </remarks>
internal class DefaultCultureDictionaryFactory : ICultureDictionaryFactory
{
private readonly ILocalizationService _localizationService;
private readonly AppCaches _appCaches;
public DefaultCultureDictionaryFactory(ILocalizationService localizationService, AppCaches appCaches)
{
_localizationService = localizationService;
_appCaches = appCaches;
}
public ICultureDictionary CreateDictionary()
{
return new DefaultCultureDictionary(_localizationService, _appCaches.RequestCache);
}
}
}
@@ -229,9 +229,6 @@ namespace Umbraco.Core.Migrations.Install
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 32, UniqueId = 32.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLockoutDate, Name = Constants.Conventions.Member.LastLockoutDateLabel, SortOrder = 4, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 33, UniqueId = 33.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLoginDate, Name = Constants.Conventions.Member.LastLoginDateLabel, SortOrder = 5, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 34, UniqueId = 34.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastPasswordChangeDate, Name = Constants.Conventions.Member.LastPasswordChangeDateLabel, SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 35, UniqueId = 35.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.PasswordQuestion, Name = Constants.Conventions.Member.PasswordQuestionLabel, SortOrder = 7, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 36, UniqueId = 36.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.PasswordAnswer, Name = Constants.Conventions.Member.PasswordAnswerLabel, SortOrder = 8, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
}
private void CreateLanguageData()
+14 -91
View File
@@ -18,7 +18,6 @@ namespace Umbraco.Core.Models
private string _username;
private string _email;
private string _rawPasswordValue;
private object _providerUserKey;
/// <summary>
/// Constructor for creating an empty Member object
@@ -166,66 +165,6 @@ namespace Umbraco.Core.Models
// * Check if we are using the umbraco membership provider, if so then we need to use the configured fields - not the explicit fields below
// * If any of the fields don't exist, what should we do? Currently it will throw an exception!
/// <summary>
/// Gets or sets the Password Question
/// </summary>
/// <remarks>
/// Alias: umbracoMemberPasswordRetrievalQuestion
/// Part of the standard properties collection.
/// </remarks>
[DataMember]
public string PasswordQuestion
{
get
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.PasswordQuestion, "PasswordQuestion", default(string));
if (a.Success == false) return a.Result;
return Properties[Constants.Conventions.Member.PasswordQuestion].GetValue() == null
? string.Empty
: Properties[Constants.Conventions.Member.PasswordQuestion].GetValue().ToString();
}
set
{
if (WarnIfPropertyTypeNotFoundOnSet(
Constants.Conventions.Member.PasswordQuestion,
"PasswordQuestion") == false) return;
Properties[Constants.Conventions.Member.PasswordQuestion].SetValue(value);
}
}
/// <summary>
/// Gets or sets the raw password answer value
/// </summary>
/// <remarks>
/// For security reasons this value should be encrypted, the encryption process is handled by the membership provider
/// Alias: umbracoMemberPasswordRetrievalAnswer
///
/// Part of the standard properties collection.
/// </remarks>
[IgnoreDataMember]
public string RawPasswordAnswerValue
{
get
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.PasswordAnswer, "PasswordAnswer", default(string));
if (a.Success == false) return a.Result;
return Properties[Constants.Conventions.Member.PasswordAnswer].GetValue() == null
? string.Empty
: Properties[Constants.Conventions.Member.PasswordAnswer].GetValue().ToString();
}
set
{
if (WarnIfPropertyTypeNotFoundOnSet(
Constants.Conventions.Member.PasswordAnswer,
"PasswordAnswer") == false) return;
Properties[Constants.Conventions.Member.PasswordAnswer].SetValue(value);
}
}
/// <summary>
/// Gets or set the comments for the member
/// </summary>
@@ -238,7 +177,7 @@ namespace Umbraco.Core.Models
{
get
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.Comments, "Comments", default(string));
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.Comments, nameof(Comments), default(string));
if (a.Success == false) return a.Result;
return Properties[Constants.Conventions.Member.Comments].GetValue() == null
@@ -249,7 +188,7 @@ namespace Umbraco.Core.Models
{
if (WarnIfPropertyTypeNotFoundOnSet(
Constants.Conventions.Member.Comments,
"Comments") == false) return;
nameof(Comments)) == false) return;
Properties[Constants.Conventions.Member.Comments].SetValue(value);
}
@@ -267,7 +206,7 @@ namespace Umbraco.Core.Models
{
get
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsApproved, "IsApproved",
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsApproved, nameof(IsApproved),
//This is the default value if the prop is not found
true);
if (a.Success == false) return a.Result;
@@ -284,7 +223,7 @@ namespace Umbraco.Core.Models
{
if (WarnIfPropertyTypeNotFoundOnSet(
Constants.Conventions.Member.IsApproved,
"IsApproved") == false) return;
nameof(IsApproved)) == false) return;
Properties[Constants.Conventions.Member.IsApproved].SetValue(value);
}
@@ -302,7 +241,7 @@ namespace Umbraco.Core.Models
{
get
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsLockedOut, "IsLockedOut", false);
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsLockedOut, nameof(IsLockedOut), false);
if (a.Success == false) return a.Result;
if (Properties[Constants.Conventions.Member.IsLockedOut].GetValue() == null) return false;
var tryConvert = Properties[Constants.Conventions.Member.IsLockedOut].GetValue().TryConvertTo<bool>();
@@ -317,7 +256,7 @@ namespace Umbraco.Core.Models
{
if (WarnIfPropertyTypeNotFoundOnSet(
Constants.Conventions.Member.IsLockedOut,
"IsLockedOut") == false) return;
nameof(IsLockedOut)) == false) return;
Properties[Constants.Conventions.Member.IsLockedOut].SetValue(value);
}
@@ -335,7 +274,7 @@ namespace Umbraco.Core.Models
{
get
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLoginDate, "LastLoginDate", default(DateTime));
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLoginDate, nameof(LastLoginDate), default(DateTime));
if (a.Success == false) return a.Result;
if (Properties[Constants.Conventions.Member.LastLoginDate].GetValue() == null) return default(DateTime);
var tryConvert = Properties[Constants.Conventions.Member.LastLoginDate].GetValue().TryConvertTo<DateTime>();
@@ -350,7 +289,7 @@ namespace Umbraco.Core.Models
{
if (WarnIfPropertyTypeNotFoundOnSet(
Constants.Conventions.Member.LastLoginDate,
"LastLoginDate") == false) return;
nameof(LastLoginDate)) == false) return;
Properties[Constants.Conventions.Member.LastLoginDate].SetValue(value);
}
@@ -368,7 +307,7 @@ namespace Umbraco.Core.Models
{
get
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastPasswordChangeDate, "LastPasswordChangeDate", default(DateTime));
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastPasswordChangeDate, nameof(LastPasswordChangeDate), default(DateTime));
if (a.Success == false) return a.Result;
if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].GetValue() == null) return default(DateTime);
var tryConvert = Properties[Constants.Conventions.Member.LastPasswordChangeDate].GetValue().TryConvertTo<DateTime>();
@@ -383,7 +322,7 @@ namespace Umbraco.Core.Models
{
if (WarnIfPropertyTypeNotFoundOnSet(
Constants.Conventions.Member.LastPasswordChangeDate,
"LastPasswordChangeDate") == false) return;
nameof(LastPasswordChangeDate)) == false) return;
Properties[Constants.Conventions.Member.LastPasswordChangeDate].SetValue(value);
}
@@ -401,7 +340,7 @@ namespace Umbraco.Core.Models
{
get
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLockoutDate, "LastLockoutDate", default(DateTime));
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLockoutDate, nameof(LastLockoutDate), default(DateTime));
if (a.Success == false) return a.Result;
if (Properties[Constants.Conventions.Member.LastLockoutDate].GetValue() == null) return default(DateTime);
var tryConvert = Properties[Constants.Conventions.Member.LastLockoutDate].GetValue().TryConvertTo<DateTime>();
@@ -416,7 +355,7 @@ namespace Umbraco.Core.Models
{
if (WarnIfPropertyTypeNotFoundOnSet(
Constants.Conventions.Member.LastLockoutDate,
"LastLockoutDate") == false) return;
nameof(LastLockoutDate)) == false) return;
Properties[Constants.Conventions.Member.LastLockoutDate].SetValue(value);
}
@@ -435,7 +374,7 @@ namespace Umbraco.Core.Models
{
get
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.FailedPasswordAttempts, "FailedPasswordAttempts", 0);
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.FailedPasswordAttempts, nameof(FailedPasswordAttempts), 0);
if (a.Success == false) return a.Result;
if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].GetValue() == null) return default(int);
var tryConvert = Properties[Constants.Conventions.Member.FailedPasswordAttempts].GetValue().TryConvertTo<int>();
@@ -450,7 +389,7 @@ namespace Umbraco.Core.Models
{
if (WarnIfPropertyTypeNotFoundOnSet(
Constants.Conventions.Member.FailedPasswordAttempts,
"FailedPasswordAttempts") == false) return;
nameof(FailedPasswordAttempts)) == false) return;
Properties[Constants.Conventions.Member.FailedPasswordAttempts].SetValue(value);
}
@@ -462,22 +401,6 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual string ContentTypeAlias => ContentType.Alias;
/// <summary>
/// User key from the Provider.
/// </summary>
/// <remarks>
/// When using standard umbraco provider this key will
/// correspond to the guid UniqueId/Key.
/// Otherwise it will the one available from the asp.net
/// membership provider.
/// </remarks>
[DataMember]
public virtual object ProviderUserKey
{
get => _providerUserKey;
set => SetPropertyValueAndDetectChanges(value, ref _providerUserKey, nameof(ProviderUserKey));
}
/* Internal experiment - only used for mapping queries.
* Adding these to have first level properties instead of the Properties collection.
*/
@@ -1,50 +0,0 @@
using System;
using System.Web.Security;
using Umbraco.Core.Composing;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
namespace Umbraco.Core.Models.Membership
{
internal static class MembershipUserExtensions
{
internal static UmbracoMembershipMember AsConcreteMembershipUser(this IMembershipUser member, string providerName, bool providerKeyAsGuid = false)
{
var membershipMember = new UmbracoMembershipMember(member, providerName, providerKeyAsGuid);
return membershipMember;
}
internal static IMembershipUser AsIMember(this UmbracoMembershipMember membershipMember)
{
var member = membershipMember;
if (member != null)
{
return member.Member;
}
throw new NotImplementedException();
}
private static MembershipScenario? _scenario = null;
/// <summary>
/// Returns the currently configured membership scenario for members in umbraco
/// </summary>
/// <value></value>
internal static MembershipScenario GetMembershipScenario(this IMemberTypeService memberTypeService)
{
if (_scenario.HasValue == false)
{
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
if (provider.IsUmbracoMembershipProvider())
{
return MembershipScenario.NativeUmbraco;
}
var memberType = memberTypeService.Get(Constants.Conventions.MemberTypes.DefaultAlias);
return memberType != null
? MembershipScenario.CustomProviderWithUmbracoLink
: MembershipScenario.StandaloneCustomProvider;
}
return _scenario.Value;
}
}
}
@@ -1,50 +0,0 @@
using System;
using System.Web.Security;
namespace Umbraco.Core.Models.Membership
{
internal class UmbracoMembershipUser<T> : MembershipUser where T : IMembershipUser
{
private T _user;
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoMembershipUser{T}"/> class.
/// </summary>
public UmbracoMembershipUser(T user)
{
_user = user;
}
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoMembershipUser{T}"/> class.
/// </summary>
/// <param name="providerName">Name of the provider.</param>
/// <param name="name">The name.</param>
/// <param name="providerUserKey">The provider user key.</param>
/// <param name="email">The email.</param>
/// <param name="passwordQuestion">The password question.</param>
/// <param name="comment">The comment.</param>
/// <param name="isApproved">if set to <c>true</c> [is approved].</param>
/// <param name="isLockedOut">if set to <c>true</c> [is locked out].</param>
/// <param name="creationDate">The creation date.</param>
/// <param name="lastLoginDate">The last login date.</param>
/// <param name="lastActivityDate">The last activity date.</param>
/// <param name="lastPasswordChangedDate">The last password changed date.</param>
/// <param name="lastLockoutDate">The last lockout date.</param>
/// <param name="fullName">The full name.</param>
/// <param name="language">The language.</param>
/// <param name="user"></param>
public UmbracoMembershipUser(string providerName, string name, object providerUserKey, string email,
string passwordQuestion, string comment, bool isApproved, bool isLockedOut,
DateTime creationDate, DateTime lastLoginDate, DateTime lastActivityDate, DateTime lastPasswordChangedDate,
DateTime lastLockoutDate, string fullName, string language, T user)
: base(providerName, name, providerUserKey, email, passwordQuestion, comment, isApproved, isLockedOut,
creationDate, lastLoginDate, lastActivityDate, lastPasswordChangedDate, lastLockoutDate)
{
_user = user;
}
#endregion
}
}
@@ -124,13 +124,6 @@ namespace Umbraco.Core.Models.Membership
#region Implementation of IMembershipUser
[IgnoreDataMember]
public object ProviderUserKey
{
get => Id;
set => throw new NotSupportedException("Cannot set the provider user key for a user");
}
[DataMember]
public DateTime? EmailConfirmedDate
{
@@ -204,13 +197,6 @@ namespace Umbraco.Core.Models.Membership
set => SetPropertyValueAndDetectChanges(value, ref _failedLoginAttempts, nameof(FailedPasswordAttempts));
}
// TODO: Figure out how to support all of this! - we cannot have NotImplementedExceptions because these get used by the IMembershipMemberService<IUser> service so
// we'll just have them as generic get/set which don't interact with the db.
[IgnoreDataMember]
public string PasswordQuestion { get; set; }
[IgnoreDataMember]
public string RawPasswordAnswerValue { get; set; }
[IgnoreDataMember]
public string Comments { get; set; }
@@ -143,8 +143,6 @@ namespace Umbraco.Core.Persistence.Factories
content.CreateDate = nodeDto.CreateDate;
content.UpdateDate = contentVersionDto.VersionDate;
content.ProviderUserKey = content.Key; // The `ProviderUserKey` is a membership provider thing
// reset dirty initial properties (U4-1946)
content.ResetDirtyProperties(false);
return content;
@@ -41,8 +41,6 @@ namespace Umbraco.Core.Persistence.Mappers
DefineMap<Member, PropertyDataDto>(nameof(Member.IsApproved), nameof(PropertyDataDto.IntegerValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.IsLockedOut), nameof(PropertyDataDto.IntegerValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.Comments), nameof(PropertyDataDto.TextValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.RawPasswordAnswerValue), nameof(PropertyDataDto.VarcharValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.PasswordQuestion), nameof(PropertyDataDto.VarcharValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.FailedPasswordAttempts), nameof(PropertyDataDto.IntegerValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.LastLockoutDate), nameof(PropertyDataDto.DateValue));
DefineMap<Member, PropertyDataDto>(nameof(Member.LastLoginDate), nameof(PropertyDataDto.DateValue));
@@ -233,10 +233,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override void PersistNewItem(IMember entity)
{
if (entity.ProviderUserKey == null)
{
entity.ProviderUserKey = entity.Key;
}
entity.AddingEntity();
var member = (Member) entity;
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web.Security;
using Newtonsoft.Json;
using NPoco;
using Umbraco.Core.Cache;
@@ -11,13 +10,11 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Scoping;
using Umbraco.Core.Security;
namespace Umbraco.Core.Persistence.Repositories.Implement
{
@@ -28,6 +25,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
{
private readonly IMapperCollection _mapperCollection;
private readonly IGlobalSettings _globalSettings;
private readonly IUserPasswordConfiguration _passwordConfiguration;
private string _passwordConfigJson;
private bool _passwordConfigInitialized;
@@ -41,23 +39,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
/// A dictionary specifying the configuration for user passwords. If this is null then no password configuration will be persisted or read.
/// </param>
/// <param name="globalSettings"></param>
public UserRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IMapperCollection mapperCollection, IGlobalSettings globalSettings)
public UserRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IMapperCollection mapperCollection, IGlobalSettings globalSettings, IUserPasswordConfiguration passwordConfiguration)
: base(scopeAccessor, appCaches, logger)
{
_mapperCollection = mapperCollection;
_globalSettings = globalSettings;
}
// for tests
internal UserRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IMapperCollection mapperCollection, IDictionary<string, string> passwordConfig, IGlobalSettings globalSettings)
: base(scopeAccessor, appCaches, logger)
{
_mapperCollection = mapperCollection;
_globalSettings = globalSettings;
_passwordConfigJson = JsonConvert.SerializeObject(passwordConfig);
_passwordConfigInitialized = true;
}
_mapperCollection = mapperCollection ?? throw new ArgumentNullException(nameof(mapperCollection));
_globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings));
_passwordConfiguration = passwordConfiguration ?? throw new ArgumentNullException(nameof(passwordConfiguration));
}
/// <summary>
/// Returns a serialized dictionary of the password configuration that is stored against the user in the database
/// </summary>
private string PasswordConfigJson
{
get
@@ -65,14 +57,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (_passwordConfigInitialized)
return _passwordConfigJson;
// TODO: this is bad
// because the membership provider we're trying to get has a dependency on the user service
// and we should not depend on services in repositories - need a way better way to do this
var userMembershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider();
var passwordConfig = userMembershipProvider == null || userMembershipProvider.PasswordFormat != MembershipPasswordFormat.Hashed
? null
: new Dictionary<string, string> { { "hashAlgorithm", Membership.HashAlgorithmType } };
var passwordConfig = new Dictionary<string, string> { { "hashAlgorithm", _passwordConfiguration.HashAlgorithmType } };
_passwordConfigJson = passwordConfig == null ? null : JsonConvert.SerializeObject(passwordConfig);
_passwordConfigInitialized = true;
return _passwordConfigJson;
@@ -507,6 +492,7 @@ ORDER BY colName";
// list the columns to save, NOTE: would be nice to not have hard coded strings here but no real good way around that
var colsToSave = new Dictionary<string, string>
{
//TODO: Change these to constants + nameof
{"userDisabled", "IsApproved"},
{"userNoConsole", "IsLockedOut"},
{"startStructureID", "StartContentId"},
@@ -1,14 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration.Grid;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
@@ -5,6 +5,7 @@ using Umbraco.Core.Composing.CompositionExtensions;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Hosting;
using Umbraco.Core.Dictionary;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
@@ -18,6 +19,7 @@ using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.PropertyEditors.Validators;
using Umbraco.Core.Scoping;
using Umbraco.Core.Security;
using Umbraco.Core.Serialization;
using Umbraco.Core.Services;
using Umbraco.Core.Strings;
@@ -131,6 +133,10 @@ namespace Umbraco.Core.Runtime
// by default, register a noop rebuilder
composition.RegisterUnique<IPublishedSnapshotRebuilder, PublishedSnapshotRebuilder>();
composition.SetCultureDictionaryFactory<DefaultCultureDictionaryFactory>();
composition.Register(f => f.GetInstance<ICultureDictionaryFactory>().CreateDictionary(), Lifetime.Singleton);
composition.RegisterUnique<IPasswordGenerator, PasswordGenerator>();
}
}
}
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Security;
using Microsoft.AspNet.Identity;
using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
@@ -34,32 +33,24 @@ namespace Umbraco.Core.Security
//IQueryableUserStore<BackOfficeIdentityUser, int>
{
private readonly IUserService _userService;
private readonly IMemberTypeService _memberTypeService;
private readonly IEntityService _entityService;
private readonly IExternalLoginService _externalLoginService;
private readonly IGlobalSettings _globalSettings;
private readonly UmbracoMapper _mapper;
private bool _disposed = false;
public BackOfficeUserStore(IUserService userService, IMemberTypeService memberTypeService, IEntityService entityService, IExternalLoginService externalLoginService, IGlobalSettings globalSettings, MembershipProviderBase usersMembershipProvider, UmbracoMapper mapper)
public BackOfficeUserStore(IUserService userService, IEntityService entityService, IExternalLoginService externalLoginService, IGlobalSettings globalSettings, UmbracoMapper mapper)
{
_userService = userService;
_memberTypeService = memberTypeService;
_entityService = entityService;
_externalLoginService = externalLoginService;
_globalSettings = globalSettings;
if (userService == null) throw new ArgumentNullException("userService");
if (usersMembershipProvider == null) throw new ArgumentNullException("usersMembershipProvider");
if (externalLoginService == null) throw new ArgumentNullException("externalLoginService");
_mapper = mapper;
_userService = userService;
_externalLoginService = externalLoginService;
if (usersMembershipProvider.PasswordFormat != MembershipPasswordFormat.Hashed)
{
throw new InvalidOperationException("Cannot use ASP.Net Identity with UmbracoMembersUserStore when the password format is not Hashed");
}
}
/// <summary>
@@ -0,0 +1,20 @@
using Microsoft.AspNet.Identity;
using Umbraco.Core.Configuration;
namespace Umbraco.Core.Security
{
/// <summary>
/// Ensure that both the normal password validator rules are processed along with the underlying membership provider rules
/// </summary>
public class ConfiguredPasswordValidator : PasswordValidator
{
public ConfiguredPasswordValidator(IPasswordConfiguration config)
{
RequiredLength = config.RequiredLength;
RequireNonLetterOrDigit = config.RequireNonLetterOrDigit;
RequireDigit = config.RequireDigit;
RequireLowercase = config.RequireLowercase;
RequireUppercase = config.RequireUppercase;
}
}
}
@@ -1,12 +0,0 @@
using Microsoft.AspNet.Identity;
namespace Umbraco.Core.Security
{
/// <summary>
/// A password hasher that is based on the rules configured for a membership provider
/// </summary>
public interface IMembershipProviderPasswordHasher : IPasswordHasher
{
MembershipProviderBase MembershipProvider { get; }
}
}
@@ -16,8 +16,6 @@ namespace Umbraco.Core.Security
string CommentPropertyTypeAlias { get; }
string LastLoginPropertyTypeAlias { get; }
string LastPasswordChangedPropertyTypeAlias { get; }
string PasswordRetrievalQuestionPropertyTypeAlias { get; }
string PasswordRetrievalAnswerPropertyTypeAlias { get; }
}
}
@@ -8,7 +8,7 @@ namespace Umbraco.Core.Security
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TUser"></typeparam>
public interface IUserAwarePasswordHasher<in TUser, TKey>
public interface IUserAwarePasswordHasher<in TUser, TKey> : IPasswordHasher
where TUser : class, IUser<TKey>
where TKey : IEquatable<TKey>
{
@@ -1,10 +0,0 @@
namespace Umbraco.Core.Security
{
/// <summary>
/// A marker interface used internally to identify Umbraco built-in Users membership providers
/// </summary>
internal interface IUsersMembershipProvider
{
}
}
@@ -1,34 +0,0 @@
using Microsoft.AspNet.Identity;
namespace Umbraco.Core.Security
{
/// <summary>
/// A password hasher that conforms to the password hashing done with membership providers
/// </summary>
public class MembershipProviderPasswordHasher : IMembershipProviderPasswordHasher
{
/// <summary>
/// Exposes the underlying MembershipProvider
/// </summary>
public MembershipProviderBase MembershipProvider { get; private set; }
public MembershipProviderPasswordHasher(MembershipProviderBase provider)
{
MembershipProvider = provider;
}
public string HashPassword(string password)
{
return MembershipProvider.HashPasswordForStorage(password);
}
public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
return MembershipProvider.VerifyPassword(providedPassword, hashedPassword)
? PasswordVerificationResult.Success
: PasswordVerificationResult.Failed;
}
}
}
@@ -1,38 +0,0 @@
using System.Threading.Tasks;
using System.Web.Security;
using Microsoft.AspNet.Identity;
namespace Umbraco.Core.Security
{
/// <summary>
/// Ensure that both the normal password validator rules are processed along with the underlying membership provider rules
/// </summary>
public class MembershipProviderPasswordValidator : PasswordValidator
{
public MembershipProvider Provider { get; private set; }
public MembershipProviderPasswordValidator(MembershipProvider provider)
{
Provider = provider;
RequiredLength = Provider.MinRequiredPasswordLength;
RequireNonLetterOrDigit = Provider.MinRequiredNonAlphanumericCharacters > 0;
RequireDigit = false;
RequireLowercase = false;
RequireUppercase = false;
}
public override async Task<IdentityResult> ValidateAsync(string item)
{
var result = await base.ValidateAsync(item);
if (result.Succeeded == false)
return result;
var providerValidate = MembershipProviderBase.IsPasswordValid(item, Provider.MinRequiredNonAlphanumericCharacters, Provider.PasswordStrengthRegularExpression, Provider.MinRequiredPasswordLength);
if (providerValidate.Success == false)
{
return IdentityResult.Failed("Could not set password, password rules violated: " + providerValidate.Result);
}
return IdentityResult.Success;
}
}
}
@@ -0,0 +1,197 @@
using System;
using System.Security.Cryptography;
using System.Text;
using Umbraco.Core.Configuration;
namespace Umbraco.Core.Security
{
public class PasswordSecurity
{
private readonly IPasswordConfiguration _passwordConfiguration;
public PasswordSecurity(IPasswordConfiguration passwordConfiguration)
{
_passwordConfiguration = passwordConfiguration;
}
public string HashPasswordForStorage(string password)
{
string salt;
var hashed = EncryptOrHashNewPassword(password, out salt);
return FormatPasswordForStorage(hashed, salt);
}
public bool VerifyPassword(string password, string hashedPassword)
{
if (string.IsNullOrWhiteSpace(hashedPassword)) throw new ArgumentException("Value cannot be null or whitespace.", "hashedPassword");
return CheckPassword(password, hashedPassword);
}
/// <summary>
/// If the password format is a hashed keyed algorithm then we will pre-pend the salt used to hash the password
/// to the hashed password itself.
/// </summary>
/// <param name="pass"></param>
/// <param name="salt"></param>
/// <returns></returns>
public string FormatPasswordForStorage(string pass, string salt)
{
if (_passwordConfiguration.UseLegacyEncoding)
{
return pass;
}
return salt + pass;
}
public string EncryptOrHashPassword(string pass, string salt)
{
//if we are doing it the old way
if (_passwordConfiguration.UseLegacyEncoding)
{
return LegacyEncodePassword(pass);
}
//This is the correct way to implement this (as per the sql membership provider)
var bytes = Encoding.Unicode.GetBytes(pass);
var saltBytes = Convert.FromBase64String(salt);
byte[] inArray;
var hashAlgorithm = GetHashAlgorithm(pass);
var algorithm = hashAlgorithm as KeyedHashAlgorithm;
if (algorithm != null)
{
var keyedHashAlgorithm = algorithm;
if (keyedHashAlgorithm.Key.Length == saltBytes.Length)
{
//if the salt bytes is the required key length for the algorithm, use it as-is
keyedHashAlgorithm.Key = saltBytes;
}
else if (keyedHashAlgorithm.Key.Length < saltBytes.Length)
{
//if the salt bytes is too long for the required key length for the algorithm, reduce it
var numArray2 = new byte[keyedHashAlgorithm.Key.Length];
Buffer.BlockCopy(saltBytes, 0, numArray2, 0, numArray2.Length);
keyedHashAlgorithm.Key = numArray2;
}
else
{
//if the salt bytes is too short for the required key length for the algorithm, extend it
var numArray2 = new byte[keyedHashAlgorithm.Key.Length];
var dstOffset = 0;
while (dstOffset < numArray2.Length)
{
var count = Math.Min(saltBytes.Length, numArray2.Length - dstOffset);
Buffer.BlockCopy(saltBytes, 0, numArray2, dstOffset, count);
dstOffset += count;
}
keyedHashAlgorithm.Key = numArray2;
}
inArray = keyedHashAlgorithm.ComputeHash(bytes);
}
else
{
var buffer = new byte[saltBytes.Length + bytes.Length];
Buffer.BlockCopy(saltBytes, 0, buffer, 0, saltBytes.Length);
Buffer.BlockCopy(bytes, 0, buffer, saltBytes.Length, bytes.Length);
inArray = hashAlgorithm.ComputeHash(buffer);
}
return Convert.ToBase64String(inArray);
}
/// <summary>
/// Checks the password.
/// </summary>
/// <param name="password">The password.</param>
/// <param name="dbPassword">The dbPassword.</param>
/// <returns></returns>
public bool CheckPassword(string password, string dbPassword)
{
if (string.IsNullOrWhiteSpace(dbPassword)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(dbPassword));
var storedHashedPass = StoredPassword(dbPassword, out var salt);
var hashed = EncryptOrHashPassword(password, salt);
return storedHashedPass == hashed;
}
/// <summary>
/// Encrypt/hash a new password with a new salt
/// </summary>
/// <param name="newPassword"></param>
/// <param name="salt"></param>
/// <returns></returns>
public string EncryptOrHashNewPassword(string newPassword, out string salt)
{
salt = GenerateSalt();
return EncryptOrHashPassword(newPassword, salt);
}
/// <summary>
/// Returns the hashed password without the salt if it is hashed
/// </summary>
/// <param name="storedString"></param>
/// <param name="salt">returns the salt</param>
/// <returns></returns>
public string StoredPassword(string storedString, out string salt)
{
if (string.IsNullOrWhiteSpace(storedString)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(storedString));
if (_passwordConfiguration.UseLegacyEncoding)
{
salt = string.Empty;
return storedString;
}
var saltLen = GenerateSalt();
salt = storedString.Substring(0, saltLen.Length);
return storedString.Substring(saltLen.Length);
}
public static string GenerateSalt()
{
var numArray = new byte[16];
new RNGCryptoServiceProvider().GetBytes(numArray);
return Convert.ToBase64String(numArray);
}
public HashAlgorithm GetHashAlgorithm(string password)
{
if (_passwordConfiguration.UseLegacyEncoding)
{
return new HMACSHA1
{
//the legacy salt was actually the password :(
Key = Encoding.Unicode.GetBytes(password)
};
}
if (_passwordConfiguration.HashAlgorithmType.IsNullOrWhiteSpace())
throw new InvalidOperationException("No hash algorithm type specified");
var alg = HashAlgorithm.Create(_passwordConfiguration.HashAlgorithmType);
if (alg == null)
throw new InvalidOperationException($"The hash algorithm specified {_passwordConfiguration.HashAlgorithmType} cannot be resolved");
return alg;
}
/// <summary>
/// Encodes the password.
/// </summary>
/// <param name="password">The password.</param>
/// <returns>The encoded password.</returns>
private string LegacyEncodePassword(string password)
{
string encodedPassword = password;
var hashAlgorith = GetHashAlgorithm(password);
encodedPassword = Convert.ToBase64String(hashAlgorith.ComputeHash(Encoding.Unicode.GetBytes(password)));
return encodedPassword;
}
}
}
@@ -1,15 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Security.Claims;
using System.Security.Principal;
using System.Web;
using System.Web.Security;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
using Newtonsoft.Json;
using Umbraco.Core.Configuration;
namespace Umbraco.Core.Security
{
@@ -7,24 +7,41 @@ namespace Umbraco.Core.Security
/// <summary>
/// The default password hasher that is User aware so that it can process the hashing based on the user's settings
/// </summary>
public class UserAwareMembershipProviderPasswordHasher : MembershipProviderPasswordHasher, IUserAwarePasswordHasher<BackOfficeIdentityUser, int>
public class UserAwarePasswordHasher : IUserAwarePasswordHasher<BackOfficeIdentityUser, int>
{
public UserAwareMembershipProviderPasswordHasher(MembershipProviderBase provider) : base(provider)
private readonly PasswordSecurity _passwordSecurity;
public UserAwarePasswordHasher(PasswordSecurity passwordSecurity)
{
_passwordSecurity = passwordSecurity;
}
public string HashPassword(string password)
{
return _passwordSecurity.HashPasswordForStorage(password);
}
public string HashPassword(BackOfficeIdentityUser user, string password)
{
// TODO: Implement the logic for this, we need to lookup the password format for the user and hash accordingly: http://issues.umbraco.org/issue/U4-10089
//NOTE: For now this just falls back to the hashing we are currently using
return base.HashPassword(password);
return HashPassword(password);
}
public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
return _passwordSecurity.VerifyPassword(providedPassword, hashedPassword)
? PasswordVerificationResult.Success
: PasswordVerificationResult.Failed;
}
public PasswordVerificationResult VerifyHashedPassword(BackOfficeIdentityUser user, string hashedPassword, string providedPassword)
{
// TODO: Implement the logic for this, we need to lookup the password format for the user and hash accordingly: http://issues.umbraco.org/issue/U4-10089
//NOTE: For now this just falls back to the hashing we are currently using
return base.VerifyHashedPassword(hashedPassword, providedPassword);
return VerifyHashedPassword(hashedPassword, providedPassword);
}
}
}
@@ -1,27 +0,0 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Services
{
/// <summary>
/// Defines part of the MemberService, which is specific to methods used by the membership provider.
/// </summary>
/// <remarks>
/// Idea is to have this as an isolated interface so that it can be easily 'replaced' in the membership provider implementation.
/// </remarks>
public interface IMembershipMemberService : IMembershipMemberService<IMember>, IMembershipRoleService<IMember>
{
/// <summary>
/// Creates and persists a new Member
/// </summary>
/// <param name="username">Username of the Member to create</param>
/// <param name="email">Email of the Member to create</param>
/// <param name="memberType"><see cref="IMemberType"/> which the Member should be based on</param>
/// <returns><see cref="IMember"/></returns>
IMember CreateMemberWithIdentity(string username, string email, IMemberType memberType);
}
}
@@ -8,7 +8,6 @@ using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Scoping;
@@ -27,17 +26,16 @@ namespace Umbraco.Core.Services.Implement
private readonly IContentTypeRepository _contentTypeRepository;
private readonly IDocumentBlueprintRepository _documentBlueprintRepository;
private readonly ILanguageRepository _languageRepository;
private readonly Lazy<IPropertyValidationService> _propertyValidationService;
private IQuery<IContent> _queryNotTrashed;
//TODO: The non-lazy object should be injected
private readonly Lazy<PropertyValidationService> _propertyValidationService = new Lazy<PropertyValidationService>(() => new PropertyValidationService());
#region Constructors
public ContentService(IScopeProvider provider, ILogger logger,
IEventMessagesFactory eventMessagesFactory,
IDocumentRepository documentRepository, IEntityRepository entityRepository, IAuditRepository auditRepository,
IContentTypeRepository contentTypeRepository, IDocumentBlueprintRepository documentBlueprintRepository, ILanguageRepository languageRepository)
IContentTypeRepository contentTypeRepository, IDocumentBlueprintRepository documentBlueprintRepository, ILanguageRepository languageRepository,
Lazy<IPropertyValidationService> propertyValidationService)
: base(provider, logger, eventMessagesFactory)
{
_documentRepository = documentRepository;
@@ -46,6 +44,7 @@ namespace Umbraco.Core.Services.Implement
_contentTypeRepository = contentTypeRepository;
_documentBlueprintRepository = documentBlueprintRepository;
_languageRepository = languageRepository;
_propertyValidationService = propertyValidationService;
}
#endregion
@@ -1,19 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Security;
using Umbraco.Core.Composing;
using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Scoping;
using Umbraco.Core.Security;
namespace Umbraco.Core.Services.Implement
{
@@ -29,9 +25,6 @@ namespace Umbraco.Core.Services.Implement
private readonly IMemberGroupService _memberGroupService;
//only for unit tests!
internal MembershipProviderBase MembershipProvider { get; set; }
#region Constructor
public MemberService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMemberGroupService memberGroupService,
@@ -71,11 +64,7 @@ namespace Umbraco.Core.Services.Implement
{
case MemberCountType.All:
query = Query<IMember>();
break;
case MemberCountType.Online:
var fromDate = DateTime.Now.AddMinutes(-Membership.UserIsOnlineTimeWindow);
query = Query<IMember>().Where(x => ((Member) x).PropertyTypeAlias == Constants.Conventions.Member.LastLoginDate && ((Member) x).DateTimePropertyValue > fromDate);
break;
break;
case MemberCountType.LockedOut:
query = Query<IMember>().Where(x => ((Member) x).PropertyTypeAlias == Constants.Conventions.Member.IsLockedOut && ((Member) x).BoolPropertyValue);
break;
@@ -1130,39 +1119,6 @@ namespace Umbraco.Core.Services.Implement
#region Membership
/// <summary>
/// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method
/// </summary>
/// <remarks>This method exists so that Umbraco developers can use one entry point to create/update
/// Members if they choose to. </remarks>
/// <param name="member">The Member to save the password for</param>
/// <param name="password">The password to encrypt and save</param>
public void SavePassword(IMember member, string password)
{
if (member == null) throw new ArgumentNullException(nameof(member));
var provider = MembershipProvider ?? MembershipProviderExtensions.GetMembersMembershipProvider();
if (provider.IsUmbracoMembershipProvider())
provider.ChangePassword(member.Username, "", password); // this is actually updating the password
else
throw new NotSupportedException("When using a non-Umbraco membership provider you must change the member password by using the MembershipProvider.ChangePassword method");
// go re-fetch the member to update the properties that may have changed
// check that it still exists (optimistic concurrency somehow)
// re-fetch and ensure it exists
var m = GetByUsername(member.Username);
if (m == null) return; // gone
// update properties that have changed
member.RawPasswordValue = m.RawPasswordValue;
member.LastPasswordChangeDate = m.LastPasswordChangeDate;
member.UpdateDate = m.UpdateDate;
// no need to save anything - provider.ChangePassword has done the updates,
// and then all we do is re-fetch to get the updated values, and update the
// in-memory member accordingly
}
/// <summary>
/// A helper method that will create a basic/generic member for use with a generic membership provider
@@ -1279,13 +1235,6 @@ namespace Umbraco.Core.Services.Implement
foreach (var property in member.Properties)
{
//ignore list
switch (property.Alias)
{
case Constants.Conventions.Member.PasswordQuestion:
continue;
}
var propertyExportModel = new MemberExportProperty
{
Id = property.Id,
@@ -1,17 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Collections;
using Umbraco.Core.Composing;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Core.Services
{
//TODO: We should make this an interface and inject it into the ContentService
internal class PropertyValidationService
internal class PropertyValidationService : IPropertyValidationService
{
private readonly PropertyEditorCollection _propertyEditors;
private readonly IDataTypeService _dataTypeService;
@@ -22,15 +15,7 @@ namespace Umbraco.Core.Services
_dataTypeService = dataTypeService;
}
//TODO: Remove this method in favor of the overload specifying all dependencies
public PropertyValidationService()
: this(Current.PropertyEditors, Current.Services.DataTypeService)
{
}
/// <summary>
/// Validates the content item's properties pass validation rules
/// </summary>
/// <inheritdoc />
public bool IsPropertyDataValid(IContent content, out IProperty[] invalidProperties, CultureImpact impact)
{
// select invalid properties
@@ -63,9 +48,7 @@ namespace Umbraco.Core.Services
return invalidProperties.Length == 0;
}
/// <summary>
/// Gets a value indicating whether the property has valid values.
/// </summary>
/// <inheritdoc />
public bool IsPropertyValid(IProperty property, string culture = "*", string segment = "*")
{
//NOTE - the pvalue and vvalues logic in here is borrowed directly from the Property.Values setter so if you are wondering what that's all about, look there.
@@ -1,9 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Common;
using System.Data.SqlClient;
using System.Data.SqlServerCe;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
@@ -12,12 +9,10 @@ using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.Repositories.Implement;
using Umbraco.Core.Scoping;
using Umbraco.Core.Security;
namespace Umbraco.Core.Services.Implement
{
@@ -479,23 +474,7 @@ namespace Umbraco.Core.Services.Implement
{
case MemberCountType.All:
query = Query<IUser>();
break;
case MemberCountType.Online:
throw new NotImplementedException();
//var fromDate = DateTime.Now.AddMinutes(-Membership.UserIsOnlineTimeWindow);
//query =
// Query<IMember>.Builder.Where(
// x =>
// ((Member)x).PropertyTypeAlias == Constants.Conventions.Member.LastLoginDate &&
// ((Member)x).DateTimePropertyValue > fromDate);
//return repository.GetCountByQuery(query);
//var fromDate = DateTime.Now.AddMinutes(-Membership.UserIsOnlineTimeWindow);
//query =
// Query<IMember>.Builder.Where(
// x =>
// ((Member)x).PropertyTypeAlias == Constants.Conventions.Member.LastLoginDate &&
// ((Member)x).DateTimePropertyValue > fromDate);
//return repository.GetCountByQuery(query);
break;
case MemberCountType.LockedOut:
query = Query<IUser>().Where(x => x.IsLockedOut);
break;
File diff suppressed because it is too large Load Diff
+7 -22
View File
@@ -146,6 +146,8 @@
<Compile Include="CompositionExtensions_FileSystems.cs" />
<Compile Include="ConventionsHelper.cs" />
<Compile Include="Deploy\IGridCellValueConnector.cs" />
<Compile Include="Dictionary\UmbracoCultureDictionary.cs" />
<Compile Include="Dictionary\UmbracoCultureDictionaryFactory.cs" />
<Compile Include="Events\ContentPublishedEventArgs.cs" />
<Compile Include="Events\ContentPublishingEventArgs.cs" />
<Compile Include="Events\ContentSavedEventArgs.cs" />
@@ -201,7 +203,6 @@
<Compile Include="Models\PropertyCollection.cs" />
<Compile Include="Models\PropertyType.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\FixLanguageIsoCodeLength.cs" />
<Compile Include="Models\CultureImpact.cs" />
<Compile Include="Models\PublishedContent\IndexedArrayItem.cs" />
<Compile Include="Models\PublishedContent\VariationContextAccessorExtensions.cs" />
<Compile Include="Models\Template.cs" />
@@ -231,13 +232,9 @@
<Compile Include="Migrations\Upgrade\V_8_0_0\RenameLabelAndRichTextPropertyEditorAliases.cs" />
<Compile Include="PropertyEditors\VoidEditor.cs" />
<Compile Include="PublishedContentExtensions.cs" />
<Compile Include="Security\PasswordSecurity.cs" />
<Compile Include="Serialization\JsonNetSerializer.cs" />
<Compile Include="Services\Changes\ContentTypeChange.cs" />
<Compile Include="Services\Changes\ContentTypeChangeExtensions.cs" />
<Compile Include="Services\IMemberService.cs" />
<Compile Include="Services\IMembershipMemberService.cs" />
<Compile Include="Services\LocalizedTextServiceExtensions.cs" />
<Compile Include="Services\PropertyValidationService.cs" />
<Compile Include="Services\Implement\PropertyValidationService.cs" />
<Compile Include="StringExtensions.cs" />
<Compile Include="Strings\DefaultUrlSegmentProvider.cs" />
<Compile Include="Strings\IUrlSegmentProvider.cs" />
@@ -372,9 +369,6 @@
<Compile Include="Models\MediaExtensions.cs" />
<Compile Include="Models\MediaType.cs" />
<Compile Include="Models\Member.cs" />
<Compile Include="Models\Membership\MembershipUserExtensions.cs" />
<Compile Include="Models\Membership\UmbracoMembershipMember.cs" />
<Compile Include="Models\Membership\UmbracoMembershipUser.cs" />
<Compile Include="Models\Membership\User.cs" />
<Compile Include="Models\MemberType.cs" />
<Compile Include="Models\PublishedContent\PublishedContentTypeFactory.cs" />
@@ -649,7 +643,6 @@
<Compile Include="Persistence\Querying\SqlExpressionExtensions.cs" />
<Compile Include="Persistence\Querying\SqlTranslator.cs" />
<Compile Include="Persistence\Querying\TextColumnType.cs" />
<Compile Include="Persistence\Querying\ValuePropertyMatchType.cs" />
<Compile Include="Persistence\RecordPersistenceType.cs" />
<Compile Include="Persistence\Repositories\Implement\AuditRepository.cs" />
<Compile Include="Persistence\Repositories\Implement\DocumentBlueprintRepository.cs" />
@@ -743,20 +736,14 @@
<Compile Include="Security\BackOfficeUserValidator.cs" />
<Compile Include="Security\ContentPermissionsHelper.cs" />
<Compile Include="Security\EmailService.cs" />
<Compile Include="Security\IMembershipProviderPasswordHasher.cs" />
<Compile Include="Security\IUmbracoMemberTypeMembershipProvider.cs" />
<Compile Include="Security\IUserAwarePasswordHasher.cs" />
<Compile Include="Security\IUserSessionStore.cs" />
<Compile Include="Security\IUsersMembershipProvider.cs" />
<Compile Include="Security\MachineKeyGenerator.cs" />
<Compile Include="Security\MembershipProviderBase.cs" />
<Compile Include="Security\MembershipProviderExtensions.cs" />
<Compile Include="Security\MembershipProviderPasswordHasher.cs" />
<Compile Include="Security\MembershipProviderPasswordValidator.cs" />
<Compile Include="Security\ConfiguredPasswordValidator.cs" />
<Compile Include="Security\UmbracoBackOfficeIdentity.cs" />
<Compile Include="Security\UmbracoEmailMessage.cs" />
<Compile Include="Security\UmbracoMembershipProviderBase.cs" />
<Compile Include="Security\UserAwareMembershipProviderPasswordHasher.cs" />
<Compile Include="Security\UserAwarePasswordHasher.cs" />
<Compile Include="Serialization\CaseInsensitiveDictionaryConverter.cs" />
<Compile Include="Serialization\ForceInt32Converter.cs" />
<Compile Include="Serialization\JsonReadConverter.cs" />
@@ -796,16 +783,13 @@
<Compile Include="Services\Implement\NotificationService.cs" />
<Compile Include="Services\Implement\PackagingService.cs" />
<Compile Include="Services\Implement\PublicAccessService.cs" />
<Compile Include="Services\PublicAccessServiceExtensions.cs" />
<Compile Include="Services\Implement\RedirectUrlService.cs" />
<Compile Include="Services\Implement\RelationService.cs" />
<Compile Include="Services\Implement\RepositoryService.cs" />
<Compile Include="Services\Implement\ScopeRepositoryService.cs" />
<Compile Include="Services\Implement\ServerRegistrationService.cs" />
<Compile Include="Services\ServiceContext.cs" />
<Compile Include="Services\Implement\TagService.cs" />
<Compile Include="Services\Implement\UserService.cs" />
<Compile Include="Services\UserServiceExtensions.cs" />
<Compile Include="Compose\ManifestWatcherComponent.cs" />
<Compile Include="Compose\RelateOnCopyComponent.cs" />
<Compile Include="Compose\RelateOnTrashComponent.cs" />
@@ -833,5 +817,6 @@
<Name>Umbraco.Abstractions</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
-1
View File
@@ -43,7 +43,6 @@
<providers>
<clear/>
<add name="UmbracoMembershipProvider" type="Umbraco.Web.Security.Providers.MembersMembershipProvider, Umbraco.Web" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="4" useLegacyEncoding="false" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="Member" passwordFormat="Hashed"/>
<add name="UsersMembershipProvider" type="Umbraco.Web.Security.Providers.UsersMembershipProvider, Umbraco.Web" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="4" useLegacyEncoding="false" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" passwordFormat="Hashed" allowManuallyChangingPassword="false"/>
</providers>
</membership>
</system.web>
@@ -31,11 +31,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
return _requestCache.GetCacheItem<IPublishedContent>(
GetCacheKey("GetByProviderKey", key), () =>
{
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
if (provider.IsUmbracoMembershipProvider() == false)
{
throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active");
}
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
var result = _memberService.GetByProviderKey(key);
if (result == null) return null;
@@ -49,11 +45,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
return _requestCache.GetCacheItem<IPublishedContent>(
GetCacheKey("GetById", memberId), () =>
{
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
if (provider.IsUmbracoMembershipProvider() == false)
{
throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active");
}
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
var result = _memberService.GetById(memberId);
if (result == null) return null;
@@ -67,11 +59,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
return _requestCache.GetCacheItem<IPublishedContent>(
GetCacheKey("GetByUsername", username), () =>
{
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
if (provider.IsUmbracoMembershipProvider() == false)
{
throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active");
}
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
var result = _memberService.GetByUsername(username);
if (result == null) return null;
@@ -85,11 +73,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache
return _requestCache.GetCacheItem<IPublishedContent>(
GetCacheKey("GetByEmail", email), () =>
{
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
if (provider.IsUmbracoMembershipProvider() == false)
{
throw new NotSupportedException("Cannot access this method unless the Umbraco membership provider is active");
}
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
var result = _memberService.GetByEmail(email);
if (result == null) return null;
@@ -6,8 +6,8 @@ using System.Web.Security;
using Moq;
using NUnit.Framework;
using Umbraco.Core.Security;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.Testing;
using Umbraco.Web.Security;
namespace Umbraco.Tests.Membership
{
@@ -18,7 +18,7 @@ namespace Umbraco.Tests.Membership
[Test]
public void Change_Password_Without_AllowManuallyChangingPassword_And_No_Pass_Validation()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var providerMock = new Mock<MembershipProviderBase>() { CallBase = true };
providerMock.Setup(@base => @base.AllowManuallyChangingPassword).Returns(false);
var provider = providerMock.Object;
@@ -28,7 +28,7 @@ namespace Umbraco.Tests.Membership
[Test]
public void Change_Password_With_AllowManuallyChangingPassword_And_Invalid_Creds()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var providerMock = new Mock<MembershipProviderBase>() { CallBase = true };
providerMock.Setup(@base => @base.AllowManuallyChangingPassword).Returns(false);
providerMock.Setup(@base => @base.ValidateUser("test", "test")).Returns(false);
var provider = providerMock.Object;
@@ -40,7 +40,7 @@ namespace Umbraco.Tests.Membership
[Test]
public void ChangePasswordQuestionAndAnswer_Without_RequiresQuestionAndAnswer()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var providerMock = new Mock<MembershipProviderBase>() { CallBase = true };
providerMock.Setup(@base => @base.RequiresQuestionAndAnswer).Returns(false);
var provider = providerMock.Object;
@@ -50,7 +50,7 @@ namespace Umbraco.Tests.Membership
[Test]
public void ChangePasswordQuestionAndAnswer_Without_AllowManuallyChangingPassword_And_Invalid_Creds()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var providerMock = new Mock<MembershipProviderBase>() { CallBase = true };
providerMock.Setup(@base => @base.RequiresQuestionAndAnswer).Returns(true);
providerMock.Setup(@base => @base.AllowManuallyChangingPassword).Returns(false);
providerMock.Setup(@base => @base.ValidateUser("test", "test")).Returns(false);
@@ -62,7 +62,7 @@ namespace Umbraco.Tests.Membership
[Test]
public void CreateUser_Not_Whitespace()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) {CallBase = true};
var providerMock = new Mock<MembershipProviderBase>() {CallBase = true};
var provider = providerMock.Object;
MembershipCreateStatus status;
@@ -75,7 +75,7 @@ namespace Umbraco.Tests.Membership
[Test]
public void CreateUser_Invalid_Question()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var providerMock = new Mock<MembershipProviderBase>() { CallBase = true };
providerMock.Setup(@base => @base.RequiresQuestionAndAnswer).Returns(true);
var provider = providerMock.Object;
@@ -89,7 +89,7 @@ namespace Umbraco.Tests.Membership
[Test]
public void CreateUser_Invalid_Answer()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var providerMock = new Mock<MembershipProviderBase>() { CallBase = true };
providerMock.Setup(@base => @base.RequiresQuestionAndAnswer).Returns(true);
var provider = providerMock.Object;
@@ -103,7 +103,7 @@ namespace Umbraco.Tests.Membership
[Test]
public void GetPassword_Without_EnablePasswordRetrieval()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var providerMock = new Mock<MembershipProviderBase>() { CallBase = true };
providerMock.Setup(@base => @base.EnablePasswordRetrieval).Returns(false);
var provider = providerMock.Object;
@@ -113,7 +113,7 @@ namespace Umbraco.Tests.Membership
[Test]
public void GetPassword_With_Hashed()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var providerMock = new Mock<MembershipProviderBase>() { CallBase = true };
providerMock.Setup(@base => @base.EnablePasswordRetrieval).Returns(true);
providerMock.Setup(@base => @base.PasswordFormat).Returns(MembershipPasswordFormat.Hashed);
var provider = providerMock.Object;
@@ -128,7 +128,7 @@ namespace Umbraco.Tests.Membership
[Ignore("makes no sense?")]
public void ResetPassword_Without_EnablePasswordReset()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var providerMock = new Mock<MembershipProviderBase>() { CallBase = true };
providerMock.Setup(@base => @base.EnablePasswordReset).Returns(false);
var provider = providerMock.Object;
@@ -138,12 +138,12 @@ namespace Umbraco.Tests.Membership
[Test]
public void Sets_Defaults()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var providerMock = new Mock<MembershipProviderBase>() { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection());
Assert.AreEqual("test", provider.Name);
Assert.AreEqual(MembershipProviderBase.GetDefaultAppName(TestHelper.GetHostingEnvironment()), provider.ApplicationName);
Assert.AreEqual(MembershipProviderBase.GetDefaultAppName(), provider.ApplicationName);
Assert.AreEqual(false, provider.EnablePasswordRetrieval);
Assert.AreEqual(true, provider.EnablePasswordReset);
Assert.AreEqual(false, provider.RequiresQuestionAndAnswer);
@@ -160,7 +160,7 @@ namespace Umbraco.Tests.Membership
[Test]
public void Throws_Exception_With_Hashed_Password_And_Password_Retrieval()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var providerMock = new Mock<MembershipProviderBase>() { CallBase = true };
var provider = providerMock.Object;
Assert.Throws<ProviderException>(() => provider.Initialize("test", new NameValueCollection()
@@ -189,223 +189,9 @@ namespace Umbraco.Tests.Membership
Assert.AreEqual(pass, result.Success);
}
/// <summary>
/// The salt generated is always the same length
/// </summary>
[Test]
public void Check_Salt_Length()
{
var lastLength = 0;
for (var i = 0; i < 10000; i++)
{
var result = MembershipProviderBase.GenerateSalt();
if (i > 0)
{
Assert.AreEqual(lastLength, result.Length);
}
lastLength = result.Length;
}
}
[Test]
public void Get_Stored_Password_Hashed()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Hashed" }, { "hashAlgorithmType", "HMACSHA256" } });
var salt = MembershipProviderBase.GenerateSalt();
var stored = salt + "ThisIsAHashedPassword";
string initSalt;
var result = provider.StoredPassword(stored, out initSalt);
Assert.AreEqual("ThisIsAHashedPassword", result);
}
[Test]
public void Get_Stored_Password_Encrypted()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Encrypted" } });
var stored = "ThisIsAnEncryptedPassword";
string initSalt;
var result = provider.StoredPassword(stored, out initSalt);
Assert.AreEqual("ThisIsAnEncryptedPassword", result);
}
[Test]
public void Get_Stored_Password_Clear()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Clear" } });
var salt = MembershipProviderBase.GenerateSalt();
var stored = "ThisIsAClearPassword";
string initSalt;
var result = provider.StoredPassword(stored, out initSalt);
Assert.AreEqual("ThisIsAClearPassword", result);
}
[Test]
public void Format_Pass_For_Storage_Hashed()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Hashed" }, { "hashAlgorithmType", "HMACSHA256" } });
var salt = MembershipProviderBase.GenerateSalt();
var stored = "ThisIsAHashedPassword";
var result = provider.FormatPasswordForStorage(stored, salt);
Assert.AreEqual(salt + "ThisIsAHashedPassword", result);
}
[Test]
public void Format_Pass_For_Storage_Encrypted()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Encrypted" } });
var salt = MembershipProviderBase.GenerateSalt();
var stored = "ThisIsAnEncryptedPassword";
var result = provider.FormatPasswordForStorage(stored, salt);
Assert.AreEqual("ThisIsAnEncryptedPassword", result);
}
[Test]
public void Format_Pass_For_Storage_Clear()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Clear" } });
var salt = MembershipProviderBase.GenerateSalt();
var stored = "ThisIsAClearPassword";
var result = provider.FormatPasswordForStorage(stored, salt);
Assert.AreEqual("ThisIsAClearPassword", result);
}
[Test]
public void Check_Password_Hashed_KeyedHashAlgorithm()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Hashed" }, { "hashAlgorithmType", "HMACSHA256" } });
string salt;
var pass = "ThisIsAHashedPassword";
var hashed = provider.EncryptOrHashNewPassword(pass, out salt);
var storedPassword = provider.FormatPasswordForStorage(hashed, salt);
var result = provider.CheckPassword("ThisIsAHashedPassword", storedPassword);
Assert.IsTrue(result);
}
[Test]
public void Check_Password_Hashed_Non_KeyedHashAlgorithm()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Hashed" } });
string salt;
var pass = "ThisIsAHashedPassword";
var hashed = provider.EncryptOrHashNewPassword(pass, out salt);
var storedPassword = provider.FormatPasswordForStorage(hashed, salt);
var result = provider.CheckPassword("ThisIsAHashedPassword", storedPassword);
Assert.IsTrue(result);
}
[Test]
public void Check_Password_Encrypted()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Encrypted" } });
string salt;
var pass = "ThisIsAnEncryptedPassword";
var encrypted = provider.EncryptOrHashNewPassword(pass, out salt);
var result = provider.CheckPassword("ThisIsAnEncryptedPassword", encrypted);
Assert.IsTrue(result);
}
[Test]
public void Check_Password_Clear()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Clear" } });
var pass = "ThisIsAClearPassword";
var result = provider.CheckPassword("ThisIsAClearPassword", pass);
Assert.IsTrue(result);
}
[Test]
public void Can_Decrypt_Password()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Encrypted" } });
string salt;
var pass = "ThisIsAnEncryptedPassword";
var encrypted = provider.EncryptOrHashNewPassword(pass, out salt);
var result = provider.DecryptPassword(encrypted);
Assert.AreEqual(pass, result);
}
[Test]
public void Get_Hash_Algorithm_Legacy()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { {"useLegacyEncoding", "true"}, { "passwordFormat", "Hashed" }, { "hashAlgorithmType", "HMACSHA256" } });
var alg = provider.GetHashAlgorithm("blah");
Assert.IsTrue(alg is HMACSHA1);
}
[Test]
public void Get_Hash_Algorithm()
{
var providerMock = new Mock<MembershipProviderBase>(TestHelper.GetHostingEnvironment()) { CallBase = true };
var provider = providerMock.Object;
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Hashed" }, { "hashAlgorithmType", "HMACSHA256" } });
var alg = provider.GetHashAlgorithm("blah");
Assert.IsTrue(alg is HMACSHA256);
}
}
}
@@ -23,7 +23,7 @@ namespace Umbraco.Tests.Membership
{
var memberTypeServiceMock = new Mock<IMemberTypeService>();
memberTypeServiceMock.Setup(x => x.GetDefault()).Returns("Blah");
var provider = new MembersMembershipProvider(Mock.Of<IMembershipMemberService>(), memberTypeServiceMock.Object, UmbracoVersion, HostingEnvironment, IpResolver);
var provider = new MembersMembershipProvider(Mock.Of<IMembershipMemberService>(), memberTypeServiceMock.Object, TestHelper.GetUmbracoVersion());
provider.Initialize("test", new NameValueCollection());
Assert.AreEqual("Blah", provider.DefaultMemberTypeAlias);
@@ -34,7 +34,7 @@ namespace Umbraco.Tests.Membership
{
var memberTypeServiceMock = new Mock<IMemberTypeService>();
memberTypeServiceMock.Setup(x => x.GetDefault()).Returns("Blah");
var provider = new MembersMembershipProvider(Mock.Of<IMembershipMemberService>(), memberTypeServiceMock.Object, UmbracoVersion, HostingEnvironment, IpResolver);
var provider = new MembersMembershipProvider(Mock.Of<IMembershipMemberService>(), memberTypeServiceMock.Object, TestHelper.GetUmbracoVersion());
provider.Initialize("test", new NameValueCollection { { "defaultMemberTypeAlias", "Hello" } });
Assert.AreEqual("Hello", provider.DefaultMemberTypeAlias);
@@ -48,7 +48,7 @@ namespace Umbraco.Tests.Membership
var membershipServiceMock = new Mock<IMembershipMemberService>();
membershipServiceMock.Setup(service => service.Exists("test")).Returns(true);
var provider = new MembersMembershipProvider(membershipServiceMock.Object, memberTypeServiceMock.Object, UmbracoVersion, HostingEnvironment, IpResolver);
var provider = new MembersMembershipProvider(membershipServiceMock.Object, memberTypeServiceMock.Object, TestHelper.GetUmbracoVersion());
provider.Initialize("test", new NameValueCollection());
MembershipCreateStatus status;
@@ -65,7 +65,7 @@ namespace Umbraco.Tests.Membership
var membershipServiceMock = new Mock<IMembershipMemberService>();
membershipServiceMock.Setup(service => service.GetByEmail("test@test.com")).Returns(() => new Member("test", MockedContentTypes.CreateSimpleMemberType()));
var provider = new MembersMembershipProvider(membershipServiceMock.Object, memberTypeServiceMock.Object, UmbracoVersion, HostingEnvironment, IpResolver);
var provider = new MembersMembershipProvider(membershipServiceMock.Object, memberTypeServiceMock.Object, TestHelper.GetUmbracoVersion());
provider.Initialize("test", new NameValueCollection { { "requiresUniqueEmail", "true" } });
MembershipCreateStatus status;
@@ -73,73 +73,7 @@ namespace Umbraco.Tests.Membership
Assert.IsNull(user);
}
[Test]
public void Answer_Is_Encrypted()
{
IMember createdMember = null;
var memberType = MockedContentTypes.CreateSimpleMemberType();
foreach (var p in ConventionsHelper.GetStandardPropertyTypeStubs())
{
memberType.AddPropertyType(p.Value);
}
var memberTypeServiceMock = new Mock<IMemberTypeService>();
memberTypeServiceMock.Setup(x => x.GetDefault()).Returns("Member");
var membershipServiceMock = new Mock<IMembershipMemberService>();
membershipServiceMock.Setup(service => service.Exists("test")).Returns(false);
membershipServiceMock.Setup(service => service.GetByEmail("test@test.com")).Returns(() => null);
membershipServiceMock.Setup(
service => service.CreateWithIdentity(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
.Callback((string u, string e, string p, string m, bool isApproved) =>
{
createdMember = new Member("test", e, u, p, memberType, isApproved);
})
.Returns(() => createdMember);
var provider = new MembersMembershipProvider(membershipServiceMock.Object, memberTypeServiceMock.Object, UmbracoVersion, HostingEnvironment, IpResolver);
provider.Initialize("test", new NameValueCollection());
MembershipCreateStatus status;
provider.CreateUser("test", "test", "testtest$1", "test@test.com", "test", "test", true, "test", out status);
Assert.AreNotEqual("test", createdMember.RawPasswordAnswerValue);
Assert.AreEqual(provider.EncryptString("test"), createdMember.RawPasswordAnswerValue);
}
[Test]
public void Password_Encrypted()
{
IMember createdMember = null;
var memberType = MockedContentTypes.CreateSimpleMemberType();
foreach (var p in ConventionsHelper.GetStandardPropertyTypeStubs())
{
memberType.AddPropertyType(p.Value);
}
var memberTypeServiceMock = new Mock<IMemberTypeService>();
memberTypeServiceMock.Setup(x => x.GetDefault()).Returns("Member");
var membershipServiceMock = new Mock<IMembershipMemberService>();
membershipServiceMock.Setup(service => service.Exists("test")).Returns(false);
membershipServiceMock.Setup(service => service.GetByEmail("test@test.com")).Returns(() => null);
membershipServiceMock.Setup(
service => service.CreateWithIdentity(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
.Callback((string u, string e, string p, string m, bool isApproved) =>
{
createdMember = new Member("test", e, u, p, memberType, isApproved);
})
.Returns(() => createdMember);
var provider = new MembersMembershipProvider(membershipServiceMock.Object, memberTypeServiceMock.Object, UmbracoVersion, HostingEnvironment, IpResolver);
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Encrypted" } });
MembershipCreateStatus status;
provider.CreateUser("test", "test", "testtest$1", "test@test.com", "test", "test", true, "test", out status);
Assert.AreNotEqual("test", createdMember.RawPasswordValue);
var decrypted = provider.DecryptPassword(createdMember.RawPasswordValue);
Assert.AreEqual("testtest$1", decrypted);
}
[Test]
public void Password_Hashed_With_Salt()
{
@@ -162,7 +96,7 @@ namespace Umbraco.Tests.Membership
})
.Returns(() => createdMember);
var provider = new MembersMembershipProvider(membershipServiceMock.Object, memberTypeServiceMock.Object, UmbracoVersion, HostingEnvironment, IpResolver);
var provider = new MembersMembershipProvider(membershipServiceMock.Object, memberTypeServiceMock.Object, TestHelper.GetUmbracoVersion());
provider.Initialize("test", new NameValueCollection { { "passwordFormat", "Hashed" }, { "hashAlgorithmType", "HMACSHA256" } });
@@ -172,8 +106,8 @@ namespace Umbraco.Tests.Membership
Assert.AreNotEqual("test", createdMember.RawPasswordValue);
string salt;
var storedPassword = provider.StoredPassword(createdMember.RawPasswordValue, out salt);
var hashedPassword = provider.EncryptOrHashPassword("testtest$1", salt);
var storedPassword = provider.PasswordSecurity.StoredPassword(createdMember.RawPasswordValue, out salt);
var hashedPassword = provider.PasswordSecurity.EncryptOrHashPassword("testtest$1", salt);
Assert.AreEqual(hashedPassword, storedPassword);
}
@@ -31,9 +31,6 @@ namespace Umbraco.Tests.Models.Mapping
{
base.Compose();
Composition.RegisterUnique(f => Mock.Of<ICultureDictionaryFactory>());
Composition.Register(_ => Mock.Of<ILogger>());
Composition.ComposeFileSystems();
Composition.Register(_ => Mock.Of<IDataTypeService>());
-9
View File
@@ -50,9 +50,6 @@ namespace Umbraco.Tests.Models
member.LastLockoutDate = DateTime.Now;
member.LastLoginDate = DateTime.Now;
member.LastPasswordChangeDate = DateTime.Now;
member.PasswordQuestion = "question";
member.ProviderUserKey = Guid.NewGuid();
member.RawPasswordAnswerValue = "raw answer";
member.RawPasswordValue = "raw pass";
member.SortOrder = 5;
member.Trashed = false;
@@ -89,9 +86,6 @@ namespace Umbraco.Tests.Models
Assert.AreEqual(clone.Trashed, member.Trashed);
Assert.AreEqual(clone.UpdateDate, member.UpdateDate);
Assert.AreEqual(clone.VersionId, member.VersionId);
Assert.AreEqual(clone.PasswordQuestion, member.PasswordQuestion);
Assert.AreEqual(clone.ProviderUserKey, member.ProviderUserKey);
Assert.AreEqual(clone.RawPasswordAnswerValue, member.RawPasswordAnswerValue);
Assert.AreEqual(clone.RawPasswordValue, member.RawPasswordValue);
Assert.AreNotSame(clone.Properties, member.Properties);
Assert.AreEqual(clone.Properties.Count(), member.Properties.Count());
@@ -137,9 +131,6 @@ namespace Umbraco.Tests.Models
member.LastLockoutDate = DateTime.Now;
member.LastLoginDate = DateTime.Now;
member.LastPasswordChangeDate = DateTime.Now;
member.PasswordQuestion = "question";
member.ProviderUserKey = Guid.NewGuid();
member.RawPasswordAnswerValue = "raw answer";
member.RawPasswordValue = "raw pass";
member.SortOrder = 5;
member.Trashed = false;
-6
View File
@@ -43,9 +43,6 @@ namespace Umbraco.Tests.Models
LastLoginDate = DateTime.Now,
LastPasswordChangeDate = DateTime.Now,
//Password = "test pass",
//PasswordAnswer = "answer",
PasswordQuestion = "question",
//ProviderUserKey = "user key",
SessionTimeout = 5,
StartContentIds = new[] { 3 },
StartMediaIds = new[] { 8 },
@@ -88,9 +85,6 @@ namespace Umbraco.Tests.Models
LastLoginDate = DateTime.Now,
LastPasswordChangeDate = DateTime.Now,
//Password = "test pass",
//PasswordAnswer = "answer",
PasswordQuestion = "question",
//ProviderUserKey = "user key",
SessionTimeout = 5,
StartContentIds = new[] { 3 },
StartMediaIds = new[] { 8 },
@@ -14,6 +14,7 @@ using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
using Umbraco.Tests.Testing;
using Umbraco.Core.Persistence;
using Umbraco.Core.Configuration;
namespace Umbraco.Tests.Persistence.Repositories
{
@@ -54,7 +55,7 @@ namespace Umbraco.Tests.Persistence.Repositories
private UserRepository CreateRepository(IScopeProvider provider)
{
var accessor = (IScopeAccessor) provider;
var repository = new UserRepository(accessor, AppCaches.Disabled, Logger, Mappers, TestObjects.GetGlobalSettings());
var repository = new UserRepository(accessor, AppCaches.Disabled, Logger, Mappers, TestObjects.GetGlobalSettings(), Mock.Of<IUserPasswordConfiguration>());
return repository;
}
@@ -208,7 +209,7 @@ namespace Umbraco.Tests.Persistence.Repositories
var id = user.Id;
var repository2 = new UserRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, Mock.Of<IMapperCollection>(),TestObjects.GetGlobalSettings());
var repository2 = new UserRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, Mock.Of<IMapperCollection>(),TestObjects.GetGlobalSettings(), Mock.Of<IUserPasswordConfiguration>());
repository2.Delete(user);
@@ -31,6 +31,7 @@ using Umbraco.Web.PublishedCache;
using Umbraco.Web.Runtime;
using Umbraco.Web.Security;
using Current = Umbraco.Web.Composing.Current;
using Umbraco.Web.Security.Providers;
namespace Umbraco.Tests.Routing
{
@@ -147,7 +148,7 @@ namespace Umbraco.Tests.Routing
var handler = new RenderRouteHandler(umbracoContext, new TestControllerFactory(umbracoContextAccessor, Mock.Of<ILogger>(), context =>
{
var membershipHelper = new MembershipHelper(
umbracoContext.HttpContext, Mock.Of<IPublishedMemberCache>(), Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), AppCaches.Disabled, Mock.Of<ILogger>());
umbracoContext.HttpContext, Mock.Of<IPublishedMemberCache>(), Mock.Of<MembersMembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), AppCaches.Disabled, Mock.Of<ILogger>());
return new CustomDocumentController(Factory.GetInstance<IGlobalSettings>(),
umbracoContextAccessor,
Factory.GetInstance<ServiceContext>(),
@@ -0,0 +1,111 @@
using Moq;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Configuration;
using Umbraco.Core.Security;
namespace Umbraco.Tests.Security
{
[TestFixture]
public class PasswordSecurityTests
{
[Test]
public void Get_Hash_Algorithm_Legacy()
{
var passwordSecurity = new PasswordSecurity(Mock.Of<IPasswordConfiguration>(x => x.UseLegacyEncoding == true && x.HashAlgorithmType == "HMACSHA256"));
var alg = passwordSecurity.GetHashAlgorithm("blah");
Assert.IsTrue(alg is HMACSHA1);
}
[Test]
public void Get_Hash_Algorithm_Default()
{
var passwordSecurity = new PasswordSecurity(Mock.Of<IPasswordConfiguration>(x => x.HashAlgorithmType == "HMACSHA256"));
var alg = passwordSecurity.GetHashAlgorithm("blah"); // not resolved
Assert.IsTrue(alg is HMACSHA256);
}
[Test]
public void Check_Password_Hashed_Non_KeyedHashAlgorithm()
{
var passwordSecurity = new PasswordSecurity(Mock.Of<IPasswordConfiguration>(x => x.HashAlgorithmType == "SHA256"));
string salt;
var pass = "ThisIsAHashedPassword";
var hashed = passwordSecurity.EncryptOrHashNewPassword(pass, out salt);
var storedPassword = passwordSecurity.FormatPasswordForStorage(hashed, salt);
var result = passwordSecurity.CheckPassword("ThisIsAHashedPassword", storedPassword);
Assert.IsTrue(result);
}
[Test]
public void Check_Password_Hashed_KeyedHashAlgorithm()
{
var passwordSecurity = new PasswordSecurity(Mock.Of<IPasswordConfiguration>(x => x.HashAlgorithmType == "HMACSHA256"));
string salt;
var pass = "ThisIsAHashedPassword";
var hashed = passwordSecurity.EncryptOrHashNewPassword(pass, out salt);
var storedPassword = passwordSecurity.FormatPasswordForStorage(hashed, salt);
var result = passwordSecurity.CheckPassword("ThisIsAHashedPassword", storedPassword);
Assert.IsTrue(result);
}
[Test]
public void Format_Pass_For_Storage_Hashed()
{
var passwordSecurity = new PasswordSecurity(Mock.Of<IPasswordConfiguration>(x => x.HashAlgorithmType == "HMACSHA256"));
var salt = PasswordSecurity.GenerateSalt();
var stored = "ThisIsAHashedPassword";
var result = passwordSecurity.FormatPasswordForStorage(stored, salt);
Assert.AreEqual(salt + "ThisIsAHashedPassword", result);
}
[Test]
public void Get_Stored_Password_Hashed()
{
var passwordSecurity = new PasswordSecurity(Mock.Of<IPasswordConfiguration>(x => x.HashAlgorithmType == "HMACSHA256"));
var salt = PasswordSecurity.GenerateSalt();
var stored = salt + "ThisIsAHashedPassword";
string initSalt;
var result = passwordSecurity.StoredPassword(stored, out initSalt);
Assert.AreEqual("ThisIsAHashedPassword", result);
}
/// <summary>
/// The salt generated is always the same length
/// </summary>
[Test]
public void Check_Salt_Length()
{
var lastLength = 0;
for (var i = 0; i < 10000; i++)
{
var result = PasswordSecurity.GenerateSalt();
if (i > 0)
{
Assert.AreEqual(lastLength, result.Length);
}
lastLength = result.Length;
}
}
}
}
@@ -39,14 +39,6 @@ namespace Umbraco.Tests.Services
public override void SetUp()
{
base.SetUp();
// HACK: but we have no choice until we remove the SavePassword method from IMemberService
var providerMock = new Mock<MembersMembershipProvider>(ServiceContext.MemberService, ServiceContext.MemberTypeService, TestHelper.GetUmbracoVersion(), TestHelper.GetHostingEnvironment(), new AspNetIpResolver()) { CallBase = true };
providerMock.Setup(@base => @base.AllowManuallyChangingPassword).Returns(false);
providerMock.Setup(@base => @base.PasswordFormat).Returns(MembershipPasswordFormat.Hashed);
var provider = providerMock.Object;
((MemberService)ServiceContext.MemberService).MembershipProvider = provider;
}
[Test]
@@ -73,69 +65,33 @@ namespace Umbraco.Tests.Services
// TODO: see TODO in PublishedContentType, this list contains duplicates
var aliases = new[]
{
"umbracoMemberPasswordRetrievalQuestion",
"umbracoMemberPasswordRetrievalAnswer",
"umbracoMemberComments",
"umbracoMemberFailedPasswordAttempts",
"umbracoMemberApproved",
"umbracoMemberLockedOut",
"umbracoMemberLastLockoutDate",
"umbracoMemberLastLogin",
"umbracoMemberLastPasswordChangeDate",
"Email",
"Username",
"PasswordQuestion",
"Comments",
"IsApproved",
"IsLockedOut",
"LastLockoutDate",
"CreateDate",
"LastLoginDate",
"LastPasswordChangeDate"
{
Constants.Conventions.Member.Comments,
Constants.Conventions.Member.FailedPasswordAttempts,
Constants.Conventions.Member.IsApproved,
Constants.Conventions.Member.IsLockedOut,
Constants.Conventions.Member.LastLockoutDate,
Constants.Conventions.Member.LastLoginDate,
Constants.Conventions.Member.LastPasswordChangeDate,
nameof(IMember.Email),
nameof(IMember.Username),
nameof(IMember.Comments),
nameof(IMember.IsApproved),
nameof(IMember.IsLockedOut),
nameof(IMember.LastLockoutDate),
nameof(IMember.CreateDate),
nameof(IMember.LastLoginDate),
nameof(IMember.LastPasswordChangeDate)
};
var properties = pmember.Properties.ToList();
Assert.IsTrue(properties.Select(x => x.Alias).ContainsAll(aliases));
var email = properties[aliases.IndexOf("Email")];
var email = properties[aliases.IndexOf(nameof(IMember.Email))];
Assert.AreEqual("xemail", email.GetSourceValue());
}
[Test]
public void Can_Set_Password_On_New_Member()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
ServiceContext.MemberTypeService.Save(memberType);
//this will construct a member without a password
var member = MockedMember.CreateSimpleMember(memberType, "test", "test@test.com", "test");
ServiceContext.MemberService.Save(member);
Assert.IsTrue(member.RawPasswordValue.StartsWith(Constants.Security.EmptyPasswordPrefix));
ServiceContext.MemberService.SavePassword(member, "hello123456$!");
var foundMember = ServiceContext.MemberService.GetById(member.Id);
Assert.IsNotNull(foundMember);
Assert.AreNotEqual("hello123456$!", foundMember.RawPasswordValue);
Assert.IsFalse(member.RawPasswordValue.StartsWith(Constants.Security.EmptyPasswordPrefix));
}
[Test]
public void Can_Not_Set_Password_On_Existing_Member()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
ServiceContext.MemberTypeService.Save(memberType);
//this will construct a member with a password
var member = MockedMember.CreateSimpleMember(memberType, "test", "test@test.com", "hello123456$!", "test");
ServiceContext.MemberService.Save(member);
Assert.IsFalse(member.RawPasswordValue.StartsWith(Constants.Security.EmptyPasswordPrefix));
Assert.Throws<NotSupportedException>(() => ServiceContext.MemberService.SavePassword(member, "HELLO123456$!"));
}
[Test]
public void Can_Create_Member()
{
@@ -1118,23 +1074,6 @@ namespace Umbraco.Tests.Services
Assert.AreEqual(11, found);
}
[Test]
public void Count_All_Online_Members()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
ServiceContext.MemberTypeService.Save(memberType);
var members = MockedMember.CreateSimpleMember(memberType, 10, (i, member) => member.LastLoginDate = DateTime.Now.AddMinutes(i * -2));
ServiceContext.MemberService.Save(members);
var customMember = MockedMember.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
customMember.SetValue(Constants.Conventions.Member.LastLoginDate, DateTime.Now);
ServiceContext.MemberService.Save(customMember);
var found = ServiceContext.MemberService.GetCount(MemberCountType.Online);
Assert.AreEqual(9, found);
}
[Test]
public void Count_All_Locked_Members()
{
@@ -8,6 +8,7 @@ using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Strings;
using Umbraco.Tests.Testing;
using Umbraco.Web;
namespace Umbraco.Tests.Strings
{
@@ -22,6 +22,7 @@ using Umbraco.Web.Security;
using Umbraco.Web.WebApi;
using Umbraco.Core.Logging;
using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web.Security.Providers;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
@@ -150,7 +151,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
urlHelper.Setup(provider => provider.GetUrl(It.IsAny<UmbracoContext>(), It.IsAny<IPublishedContent>(), It.IsAny<UrlMode>(), It.IsAny<string>(), It.IsAny<Uri>()))
.Returns(UrlInfo.Url("/hello/world/1234"));
var membershipHelper = new MembershipHelper(umbCtx.HttpContext, Mock.Of<IPublishedMemberCache>(), Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), AppCaches.Disabled, Mock.Of<ILogger>());
var membershipHelper = new MembershipHelper(umbCtx.HttpContext, Mock.Of<IPublishedMemberCache>(), Mock.Of<MembersMembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), AppCaches.Disabled, Mock.Of<ILogger>());
var umbHelper = new UmbracoHelper(Mock.Of<IPublishedContent>(),
Mock.Of<ITagQuery>(),
+5 -3
View File
@@ -152,10 +152,13 @@ namespace Umbraco.Tests.TestHelpers
var runtimeState = Mock.Of<IRuntimeState>();
var idkMap = new IdkMap(scopeProvider);
var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<DataEditor>()));
var localizationService = GetLazyService<ILocalizationService>(factory, c => new LocalizationService(scopeProvider, logger, eventMessagesFactory, GetRepo<IDictionaryRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<ILanguageRepository>(c)));
var userService = GetLazyService<IUserService>(factory, c => new UserService(scopeProvider, logger, eventMessagesFactory, runtimeState, GetRepo<IUserRepository>(c), GetRepo<IUserGroupRepository>(c),globalSettings));
var dataTypeService = GetLazyService<IDataTypeService>(factory, c => new DataTypeService(scopeProvider, logger, eventMessagesFactory, GetRepo<IDataTypeRepository>(c), GetRepo<IDataTypeContainerRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<IEntityRepository>(c), GetRepo<IContentTypeRepository>(c)));
var contentService = GetLazyService<IContentService>(factory, c => new ContentService(scopeProvider, logger, eventMessagesFactory, GetRepo<IDocumentRepository>(c), GetRepo<IEntityRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<IContentTypeRepository>(c), GetRepo<IDocumentBlueprintRepository>(c), GetRepo<ILanguageRepository>(c)));
var propertyValidationService = new Lazy<IPropertyValidationService>(() => new PropertyValidationService(propertyEditorCollection, dataTypeService.Value));
var contentService = GetLazyService<IContentService>(factory, c => new ContentService(scopeProvider, logger, eventMessagesFactory, GetRepo<IDocumentRepository>(c), GetRepo<IEntityRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<IContentTypeRepository>(c), GetRepo<IDocumentBlueprintRepository>(c), GetRepo<ILanguageRepository>(c), propertyValidationService));
var notificationService = GetLazyService<INotificationService>(factory, c => new NotificationService(scopeProvider, userService.Value, contentService.Value, localizationService.Value, logger, ioHelper, GetRepo<INotificationsRepository>(c), globalSettings, umbracoSettings.Content));
var serverRegistrationService = GetLazyService<IServerRegistrationService>(factory, c => new ServerRegistrationService(scopeProvider, logger, eventMessagesFactory, GetRepo<IServerRegistrationRepository>(c)));
var memberGroupService = GetLazyService<IMemberGroupService>(factory, c => new MemberGroupService(scopeProvider, logger, eventMessagesFactory, GetRepo<IMemberGroupRepository>(c)));
@@ -170,8 +173,7 @@ namespace Umbraco.Tests.TestHelpers
var macroService = GetLazyService<IMacroService>(factory, c => new MacroService(scopeProvider, logger, eventMessagesFactory, GetRepo<IMacroRepository>(c), GetRepo<IAuditRepository>(c)));
var packagingService = GetLazyService<IPackagingService>(factory, c =>
{
var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<DataEditor>()));
{
var compiledPackageXmlParser = new CompiledPackageXmlParser(new ConflictingPackageData(macroService.Value, fileService.Value), globalSettings);
return new PackagingService(
auditService.Value,
@@ -22,6 +22,7 @@ using Umbraco.Web;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
using Umbraco.Web.Security.Providers;
using Umbraco.Web.WebApi;
using Current = Umbraco.Web.Composing.Current;
@@ -70,7 +71,7 @@ namespace Umbraco.Tests.Testing.TestingTests
Mock.Of<ICultureDictionaryFactory>(),
Mock.Of<IUmbracoComponentRenderer>(),
Mock.Of<IPublishedContentQuery>(),
new MembershipHelper(umbracoContext.HttpContext, Mock.Of<IPublishedMemberCache>(), Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), AppCaches.Disabled, Mock.Of<ILogger>()));
new MembershipHelper(umbracoContext.HttpContext, Mock.Of<IPublishedMemberCache>(), Mock.Of<MembersMembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), AppCaches.Disabled, Mock.Of<ILogger>()));
Assert.Pass();
}
@@ -98,12 +99,15 @@ namespace Umbraco.Tests.Testing.TestingTests
{
var umbracoContext = TestObjects.GetUmbracoContextMock();
var membershipHelper = new MembershipHelper(umbracoContext.HttpContext, Mock.Of<IPublishedMemberCache>(), Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), AppCaches.Disabled, Mock.Of<ILogger>());
var logger = Mock.Of<IProfilingLogger>();
var memberService = Mock.Of<IMemberService>();
var memberTypeService = Mock.Of<IMemberTypeService>();
var membershipProvider = new MembersMembershipProvider(memberService, memberTypeService, Mock.Of<IUmbracoVersion>());
var membershipHelper = new MembershipHelper(umbracoContext.HttpContext, Mock.Of<IPublishedMemberCache>(), membershipProvider, Mock.Of<RoleProvider>(), memberService, memberTypeService, Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), AppCaches.Disabled, logger);
var umbracoHelper = new UmbracoHelper(Mock.Of<IPublishedContent>(), Mock.Of<ITagQuery>(), Mock.Of<ICultureDictionaryFactory>(), Mock.Of<IUmbracoComponentRenderer>(), Mock.Of<IPublishedContentQuery>(), membershipHelper);
var umbracoMapper = new UmbracoMapper(new MapDefinitionCollection(new[] { Mock.Of<IMapDefinition>() }));
// ReSharper disable once UnusedVariable
var umbracoApiController = new FakeUmbracoApiController(Mock.Of<IGlobalSettings>(), Mock.Of<IUmbracoContextAccessor>(), Mock.Of<ISqlContext>(), ServiceContext.CreatePartial(), AppCaches.NoCache, Mock.Of<IProfilingLogger>(), Mock.Of<IRuntimeState>(), umbracoHelper, umbracoMapper);
var umbracoApiController = new FakeUmbracoApiController(Mock.Of<IGlobalSettings>(), Mock.Of<IUmbracoContextAccessor>(), Mock.Of<ISqlContext>(), ServiceContext.CreatePartial(), AppCaches.NoCache, logger, Mock.Of<IRuntimeState>(), umbracoHelper, umbracoMapper);
Assert.Pass();
}
+5 -2
View File
@@ -44,6 +44,7 @@ using Umbraco.Web.Hosting;
using Umbraco.Web.Sections;
using Current = Umbraco.Core.Composing.Current;
using FileSystems = Umbraco.Core.IO.FileSystems;
using Umbraco.Core.Dictionary;
namespace Umbraco.Tests.Testing
{
@@ -136,7 +137,7 @@ namespace Umbraco.Tests.Testing
// get/merge the attributes marking the method and/or the classes
Options = TestOptionAttributeBase.GetTestOptions<UmbracoTestAttribute>();
// FIXME: align to runtimes & components - don't redo everything here
// FIXME: align to runtimes & components - don't redo everything here !!!! Yes this is getting painful
var (logger, profiler) = GetLoggers(Options.Logger);
var proflogger = new ProfilingLogger(logger, profiler);
@@ -245,7 +246,8 @@ namespace Umbraco.Tests.Testing
Composition.RegisterUnique<IContentLastChanceFinder, TestLastChanceFinder>();
Composition.RegisterUnique<IVariationContextAccessor, TestVariationContextAccessor>();
Composition.RegisterUnique<IPublishedSnapshotAccessor, TestPublishedSnapshotAccessor>();
Composition.SetCultureDictionaryFactory<DefaultCultureDictionaryFactory>();
Composition.Register(f => f.GetInstance<ICultureDictionaryFactory>().CreateDictionary(), Lifetime.Singleton);
// register back office sections in the order we want them rendered
Composition.WithCollectionBuilder<SectionCollectionBuilder>().Append<ContentSection>()
.Append<MediaSection>()
@@ -334,6 +336,7 @@ namespace Umbraco.Tests.Testing
{
Composition.Configs.Add(SettingsForTests.GetDefaultUmbracoSettings);
Composition.Configs.Add(SettingsForTests.GetDefaultGlobalSettings);
//Composition.Configs.Add<IUserPasswordConfiguration>(() => new DefaultUserPasswordConfig());
}
protected virtual void ComposeApplication(bool withApplication)
+1
View File
@@ -153,6 +153,7 @@
<Compile Include="Routing\RoutableDocumentFilterTests.cs" />
<Compile Include="Runtimes\StandaloneTests.cs" />
<Compile Include="Routing\GetContentUrlsTests.cs" />
<Compile Include="Security\PasswordSecurityTests.cs" />
<Compile Include="Services\AmbiguousEventTests.cs" />
<Compile Include="Services\ContentServiceEventTests.cs" />
<Compile Include="Services\ContentServicePublishBranchTests.cs" />
@@ -18,6 +18,7 @@ using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Mapping;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
@@ -75,6 +76,7 @@ namespace Umbraco.Tests.Web.Controllers
}
IOHelper.ForceNotHosted = true;
var usersController = new AuthenticationController(
new DefaultUserPasswordConfig(),
Factory.GetInstance<IGlobalSettings>(),
umbracoContextAccessor,
Factory.GetInstance<ISqlContext>(),
@@ -82,7 +84,8 @@ namespace Umbraco.Tests.Web.Controllers
Factory.GetInstance<AppCaches>(),
Factory.GetInstance<IProfilingLogger>(),
Factory.GetInstance<IRuntimeState>(),
helper);
helper,
Factory.GetInstance<UmbracoMapper>());
return usersController;
}
@@ -21,8 +21,6 @@ using Umbraco.Tests.Testing;
using Umbraco.Web;
using Umbraco.Web.Editors;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.PublishedCache;
using Task = System.Threading.Tasks.Task;
using Umbraco.Core.Dictionary;
using Umbraco.Web.PropertyEditors;
@@ -260,6 +258,7 @@ namespace Umbraco.Tests.Web.Controllers
var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<DataEditor>()));
var controller = new ContentController(
Factory.GetInstance<ICultureDictionary>(),
propertyEditorCollection,
Factory.GetInstance<IGlobalSettings>(),
umbracoContextAccessor,
@@ -293,6 +292,7 @@ namespace Umbraco.Tests.Web.Controllers
{
var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<DataEditor>()));
var controller = new ContentController(
Factory.GetInstance<ICultureDictionary>(),
propertyEditorCollection,
Factory.GetInstance<IGlobalSettings>(),
umbracoContextAccessor,
@@ -334,6 +334,7 @@ namespace Umbraco.Tests.Web.Controllers
var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<DataEditor>()));
var controller = new ContentController(
Factory.GetInstance<ICultureDictionary>(),
propertyEditorCollection,
Factory.GetInstance<IGlobalSettings>(),
umbracoContextAccessor,
@@ -380,6 +381,7 @@ namespace Umbraco.Tests.Web.Controllers
var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<DataEditor>()));
var controller = new ContentController(
Factory.GetInstance<ICultureDictionary>(),
propertyEditorCollection,
Factory.GetInstance<IGlobalSettings>(),
umbracoContextAccessor,
@@ -418,6 +420,7 @@ namespace Umbraco.Tests.Web.Controllers
var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<DataEditor>()));
var controller = new ContentController(
Factory.GetInstance<ICultureDictionary>(),
propertyEditorCollection,
Factory.GetInstance<IGlobalSettings>(),
umbracoContextAccessor,
@@ -462,6 +465,7 @@ namespace Umbraco.Tests.Web.Controllers
var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<DataEditor>()));
var controller = new ContentController(
Factory.GetInstance<ICultureDictionary>(),
propertyEditorCollection,
Factory.GetInstance<IGlobalSettings>(),
umbracoContextAccessor,
@@ -20,6 +20,7 @@ using Umbraco.Web.Mvc;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
using Umbraco.Web.Security.Providers;
using Current = Umbraco.Web.Composing.Current;
namespace Umbraco.Tests.Web.Mvc
@@ -120,7 +121,7 @@ namespace Umbraco.Tests.Web.Mvc
Mock.Of<ICultureDictionaryFactory>(),
Mock.Of<IUmbracoComponentRenderer>(),
Mock.Of<IPublishedContentQuery>(query => query.Content(2) == content.Object),
new MembershipHelper(umbracoContext.HttpContext, Mock.Of<IPublishedMemberCache>(), Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), AppCaches.Disabled, Mock.Of<ILogger>()));
new MembershipHelper(umbracoContext.HttpContext, Mock.Of<IPublishedMemberCache>(), Mock.Of<MembersMembershipProvider>(), Mock.Of<RoleProvider>(), Mock.Of<IMemberService>(), Mock.Of<IMemberTypeService>(), Mock.Of<IUserService>(), Mock.Of<IPublicAccessService>(), AppCaches.Disabled, Mock.Of<ILogger>()));
var ctrl = new TestSurfaceController(umbracoContextAccessor, helper);
var result = ctrl.GetContent(2) as PublishedContentResult;
@@ -90,7 +90,7 @@
$location.search('invite', null);
}),
//get the membership provider config for password policies
authResource.getMembershipProviderConfig().then(function (data) {
authResource.getPasswordConfig().then(function (data) {
vm.invitedUserPasswordModel.passwordPolicies = data;
//localize the text
@@ -198,18 +198,18 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
/**
* @ngdoc method
* @name umbraco.resources.currentUserResource#getMembershipProviderConfig
* @name umbraco.resources.currentUserResource#getPasswordConfig
* @methodOf umbraco.resources.currentUserResource
*
* @description
* Gets the configuration of the user membership provider which is used to configure the change password form
*/
getMembershipProviderConfig: function () {
getPasswordConfig: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"authenticationApiBaseUrl",
"GetMembershipProviderConfig")),
"GetPasswordConfig")),
'Failed to retrieve membership provider config');
},
@@ -122,7 +122,7 @@ angular.module("umbraco")
};
//go get the config for the membership provider and add it to the model
authResource.getMembershipProviderConfig().then(function(data) {
authResource.getPasswordConfig().then(function(data) {
$scope.changePasswordModel.config = data;
//ensure the hasPassword config option is set to true (the user of course has a password already assigned)
//this will ensure the oldPassword is shown so they can change it
@@ -122,10 +122,7 @@ function MemberEditController($scope, $routeParams, $location, appState, memberR
}
function setHeaderNameState(content) {
if(content.membershipScenario === 0) {
$scope.page.nameLocked = true;
}
$scope.page.nameLocked = true;
}
/** Just shows a simple notification that there are client side validation issues to be fixed */
@@ -151,7 +148,7 @@ function MemberEditController($scope, $routeParams, $location, appState, memberR
//anytime a user is changing a member's password without the oldPassword, we are in effect resetting it so we need to set that flag here
var passwordProp = _.find(contentEditingHelper.getAllProps($scope.content), function (e) { return e.alias === '_umb_password' });
if (passwordProp && passwordProp.value && (typeof passwordProp.value.reset !== 'undefined') && !passwordProp.value.reset) {
//so if the admin is not explicitly resetting the password, flag it for resetting if a new password is being entered
// if the admin is not explicitly resetting the password, flag it for resetting if a new password is being entered
passwordProp.value.reset = !passwordProp.value.oldPassword && passwordProp.config.allowManuallyChangingPassword;
}
@@ -80,7 +80,7 @@
vm.usernameIsEmail = Umbraco.Sys.ServerVariables.umbracoSettings.usernameIsEmail && user.email === user.username;
//go get the config for the membership provider and add it to the model
authResource.getMembershipProviderConfig().then(function (data) {
authResource.getPasswordConfig().then(function (data) {
vm.changePasswordModel.config = data;
//the user has a password if they are not states: Invited, NoCredentials
@@ -15,6 +15,12 @@
<system.web>
<compilation debug="true" xdt:Transform="SetAttributes(debug)" />
<membership>
<providers>
<add name="UsersMembershipProvider" xdt:Transform="Remove" xdt:Locator="Match(name)" />
</providers>
</membership>
</system.web>
<runtime>
-1
View File
@@ -121,7 +121,6 @@
<providers>
<clear />
<add name="UmbracoMembershipProvider" type="Umbraco.Web.Security.Providers.MembersMembershipProvider, Umbraco.Web" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="10" useLegacyEncoding="false" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="Member" passwordFormat="Hashed" allowManuallyChangingPassword="false" />
<add name="UsersMembershipProvider" type="Umbraco.Web.Security.Providers.UsersMembershipProvider, Umbraco.Web" />
</providers>
</membership>
@@ -8,6 +8,7 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Web.Security;
namespace Umbraco.Web.Controllers
{
@@ -26,12 +27,6 @@ namespace Umbraco.Web.Controllers
[ValidateUmbracoFormRouteString]
public ActionResult HandleUpdateProfile([Bind(Prefix = "profileModel")] ProfileModel model)
{
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
if (provider.IsUmbracoMembershipProvider() == false)
{
throw new NotSupportedException("Profile editing with the " + typeof(UmbProfileController) + " is not supported when not using the default Umbraco membership provider");
}
if (ModelState.IsValid == false)
{
return CurrentUmbracoPage();
@@ -1,16 +0,0 @@
namespace Umbraco.Web.Dictionary
{
/// <summary>
/// A culture dictionary factory used to create an Umbraco.Core.Dictionary.ICultureDictionary.
/// </summary>
/// <remarks>
/// In the future this will allow use to potentially store dictionary items elsewhere and allows for maximum flexibility.
/// </remarks>
internal class DefaultCultureDictionaryFactory : Umbraco.Core.Dictionary.ICultureDictionaryFactory
{
public Umbraco.Core.Dictionary.ICultureDictionary CreateDictionary()
{
return new DefaultCultureDictionary();
}
}
}
@@ -26,6 +26,7 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Web.Composing;
using IUser = Umbraco.Core.Models.Membership.IUser;
using Umbraco.Core.Mapping;
namespace Umbraco.Web.Editors
{
@@ -40,10 +41,12 @@ namespace Umbraco.Web.Editors
{
private BackOfficeUserManager<BackOfficeIdentityUser> _userManager;
private BackOfficeSignInManager _signInManager;
private readonly IUserPasswordConfiguration _passwordConfiguration;
public AuthenticationController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
public AuthenticationController(IUserPasswordConfiguration passwordConfiguration, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, UmbracoMapper umbracoMapper)
: base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, umbracoMapper)
{
_passwordConfiguration = passwordConfiguration ?? throw new ArgumentNullException(nameof(passwordConfiguration));
}
protected BackOfficeUserManager<BackOfficeIdentityUser> UserManager => _userManager
@@ -57,12 +60,9 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <returns></returns>
[WebApi.UmbracoAuthorize(requireApproval: false)]
public IDictionary<string, object> GetMembershipProviderConfig()
public IDictionary<string, object> GetPasswordConfig()
{
// TODO: Check if the current PasswordValidator is an IMembershipProviderPasswordValidator, if
// it's not than we should return some generic defaults
var provider = Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider();
return provider.GetConfiguration(Services.UserService);
return _passwordConfiguration.GetConfiguration();
}
/// <summary>
@@ -12,6 +12,7 @@ using System.Linq;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services.Implement;
using Umbraco.Web.Composing;
using Umbraco.Web.Security;
namespace Umbraco.Web.Editors.Binders
{
@@ -66,49 +67,7 @@ namespace Umbraco.Web.Editors.Binders
/// <returns></returns>
private IMember GetExisting(MemberSave model)
{
var scenario = _services.MemberTypeService.GetMembershipScenario();
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
switch (scenario)
{
case MembershipScenario.NativeUmbraco:
return GetExisting(model.Key);
case MembershipScenario.CustomProviderWithUmbracoLink:
case MembershipScenario.StandaloneCustomProvider:
default:
var membershipUser = provider.GetUser(model.Key, false);
if (membershipUser == null)
{
throw new InvalidOperationException("Could not find member with key " + model.Key);
}
// TODO: Support this scenario!
//if (scenario == MembershipScenario.CustomProviderWithUmbracoLink)
//{
// //if there's a 'Member' type then we should be able to just go get it from the db since it was created with a link
// // to our data.
// var memberType = ApplicationContext.Services.MemberTypeService.GetMemberType(Constants.Conventions.MemberTypes.Member);
// if (memberType != null)
// {
// var existing = GetExisting(model.Key);
// FilterContentTypeProperties(existing.ContentType, existing.ContentType.PropertyTypes.Select(x => x.Alias).ToArray());
// }
//}
//generate a member for a generic membership provider
//NOTE: We don't care about the password here, so just generate something
//var member = MemberService.CreateGenericMembershipProviderMember(model.Name, model.Email, model.Username, Guid.NewGuid().ToString("N"));
//var convertResult = membershipUser.ProviderUserKey.TryConvertTo<Guid>();
//if (convertResult.Success == false)
//{
// throw new InvalidOperationException("Only membership providers that store a GUID as their ProviderUserKey are supported" + model.Key);
//}
//member.Key = convertResult.Result;
var member = Current.Mapper.Map<MembershipUser, IMember>(membershipUser);
return member;
}
return GetExisting(model.Key);
}
private IMember GetExisting(Guid key)
@@ -132,46 +91,17 @@ namespace Umbraco.Web.Editors.Binders
/// </remarks>
private IMember CreateNew(MemberSave model)
{
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
if (provider.IsUmbracoMembershipProvider())
var contentType = _services.MemberTypeService.Get(model.ContentTypeAlias);
if (contentType == null)
{
var contentType = _services.MemberTypeService.Get(model.ContentTypeAlias);
if (contentType == null)
{
throw new InvalidOperationException("No member type found with alias " + model.ContentTypeAlias);
}
//remove all membership properties, these values are set with the membership provider.
FilterMembershipProviderProperties(contentType);
//return the new member with the details filled in
return new Member(model.Name, model.Email, model.Username, model.Password.NewPassword, contentType);
throw new InvalidOperationException("No member type found with alias " + model.ContentTypeAlias);
}
else
{
//A custom membership provider is configured
//NOTE: Below we are assigning the password to just a new GUID because we are not actually storing the password, however that
// field is mandatory in the database so we need to put something there.
//remove all membership properties, these values are set with the membership provider.
FilterMembershipProviderProperties(contentType);
//If the default Member type exists, we'll use that to create the IMember - that way we can associate the custom membership
// provider to our data - eventually we can support editing custom properties with a custom provider.
var memberType = _services.MemberTypeService.Get(Constants.Conventions.MemberTypes.DefaultAlias);
if (memberType != null)
{
FilterContentTypeProperties(memberType, memberType.PropertyTypes.Select(x => x.Alias).ToArray());
return new Member(model.Name, model.Email, model.Username, Guid.NewGuid().ToString("N"), memberType);
}
//generate a member for a generic membership provider
var member = MemberService.CreateGenericMembershipProviderMember(model.Name, model.Email, model.Username, Guid.NewGuid().ToString("N"));
//we'll just remove all properties here otherwise we'll end up with validation errors, we don't want to persist any property data anyways
// in this case.
memberType = _services.MemberTypeService.Get(member.ContentTypeId);
FilterContentTypeProperties(memberType, memberType.PropertyTypes.Select(x => x.Alias).ToArray());
return member;
}
//return the new member with the details filled in
return new Member(model.Name, model.Email, model.Username, model.Password.NewPassword, contentType);
}
/// <summary>
+10 -27
View File
@@ -15,7 +15,6 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Mapping;
@@ -37,6 +36,7 @@ using Umbraco.Core.Persistence;
using Umbraco.Core.Security;
using Umbraco.Web.Routing;
using Constants = Umbraco.Core.Constants;
using Umbraco.Core.Dictionary;
namespace Umbraco.Web.Editors
{
@@ -57,8 +57,8 @@ namespace Umbraco.Web.Editors
public object Domains { get; private set; }
public ContentController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
public ContentController(ICultureDictionary cultureDictionary, PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
{
_propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
_allLangs = new Lazy<IDictionary<string, ILanguage>>(() => Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.IsoCode, x => x, StringComparer.InvariantCultureIgnoreCase));
@@ -357,10 +357,10 @@ namespace Umbraco.Web.Editors
var emptyContent = Services.ContentService.Create("", parentId, contentType.Alias, Security.GetUserId().ResultOr(0));
var mapped = MapToDisplay(emptyContent);
// translate the content type name if applicable
mapped.ContentTypeName = Services.TextService.UmbracoDictionaryTranslate(mapped.ContentTypeName);
mapped.ContentTypeName = Services.TextService.UmbracoDictionaryTranslate(CultureDictionary, mapped.ContentTypeName);
// if your user type doesn't have access to the Settings section it would not get this property mapped
if (mapped.DocumentType != null)
mapped.DocumentType.Name = Services.TextService.UmbracoDictionaryTranslate(mapped.DocumentType.Name);
mapped.DocumentType.Name = Services.TextService.UmbracoDictionaryTranslate(CultureDictionary, mapped.DocumentType.Name);
//remove the listview app if it exists
mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "umbListView").ToList();
@@ -2309,28 +2309,11 @@ namespace Umbraco.Web.Editors
.Where(rule => rule.RuleType == Constants.Conventions.PublicAccess.MemberUsernameRuleType)
.Select(rule => rule.RuleValue).ToArray();
MemberDisplay[] members;
switch (Services.MemberTypeService.GetMembershipScenario())
{
case MembershipScenario.NativeUmbraco:
members = usernames
.Select(username => Services.MemberService.GetByUsername(username))
.Where(member => member != null)
.Select(Mapper.Map<MemberDisplay>)
.ToArray();
break;
// TODO: test support custom membership providers
case MembershipScenario.CustomProviderWithUmbracoLink:
case MembershipScenario.StandaloneCustomProvider:
default:
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
members = usernames
.Select(username => provider.GetUser(username, false))
.Where(membershipUser => membershipUser != null)
.Select(Mapper.Map<MembershipUser, MemberDisplay>)
.ToArray();
break;
}
var members = usernames
.Select(username => Services.MemberService.GetByUsername(username))
.Where(member => member != null)
.Select(Mapper.Map<MemberDisplay>)
.ToArray();
var allGroups = Services.MemberGroupService.GetAll().ToArray();
var groups = entry.Rules
@@ -6,6 +6,7 @@ using System.Web.Http;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Editors;
@@ -26,9 +27,12 @@ namespace Umbraco.Web.Editors
[JsonDateTimeFormatAttribute]
public abstract class ContentControllerBase : BackOfficeNotificationsController
{
protected ContentControllerBase(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
protected ICultureDictionary CultureDictionary { get; }
protected ContentControllerBase(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
{
CultureDictionary = cultureDictionary;
}
protected HttpResponseMessage HandleContentNotFound(object id, bool throwException = true)
@@ -52,14 +52,14 @@ namespace Umbraco.Web.Editors
private readonly IScopeProvider _scopeProvider;
public ContentTypeController(IEntityXmlSerializer serializer,
ICultureDictionaryFactory cultureDictionaryFactory,
ICultureDictionary cultureDictionary,
IGlobalSettings globalSettings,
IUmbracoContextAccessor umbracoContextAccessor,
ISqlContext sqlContext, PropertyEditorCollection propertyEditors,
ServiceContext services, AppCaches appCaches,
IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper,
IScopeProvider scopeProvider)
: base(cultureDictionaryFactory, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
: base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
{
_serializer = serializer;
_propertyEditors = propertyEditors;
@@ -438,8 +438,8 @@ namespace Umbraco.Web.Editors
var localizedTextService = Services.TextService;
foreach (var basic in basics)
{
basic.Name = localizedTextService.UmbracoDictionaryTranslate(basic.Name);
basic.Description = localizedTextService.UmbracoDictionaryTranslate(basic.Description);
basic.Name = localizedTextService.UmbracoDictionaryTranslate(CultureDictionary, basic.Name);
basic.Description = localizedTextService.UmbracoDictionaryTranslate(CultureDictionary, basic.Description);
}
//map the blueprints
@@ -28,16 +28,13 @@ namespace Umbraco.Web.Editors
public abstract class ContentTypeControllerBase<TContentType> : UmbracoAuthorizedJsonController
where TContentType : class, IContentTypeComposition
{
private readonly ICultureDictionaryFactory _cultureDictionaryFactory;
private ICultureDictionary _cultureDictionary;
protected ContentTypeControllerBase(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
protected ContentTypeControllerBase(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
{
_cultureDictionaryFactory = cultureDictionaryFactory;
CultureDictionary = cultureDictionary;
}
protected ICultureDictionary CultureDictionary { get; }
/// <summary>
/// Returns the available composite content types for a given content type
@@ -551,9 +548,6 @@ namespace Umbraco.Web.Editors
forDisplay.Errors = ModelState.ToErrorDictionary();
return new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
}
private ICultureDictionary CultureDictionary
=> _cultureDictionary ?? (_cultureDictionary = _cultureDictionaryFactory.CreateDictionary());
}
}
}
@@ -11,6 +11,7 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Security;
namespace Umbraco.Web.Editors.Filters
{
@@ -52,7 +53,7 @@ namespace Umbraco.Web.Editors.Filters
}
//default provider!
var membershipProvider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
var membershipProvider = MembershipProviderExtensions.GetMembersMembershipProvider();
var validEmail = ValidateUniqueEmail(model, membershipProvider);
if (validEmail == false)
+3 -2
View File
@@ -36,6 +36,7 @@ using Umbraco.Web.ContentApps;
using Umbraco.Web.Editors.Binders;
using Umbraco.Web.Editors.Filters;
using Constants = Umbraco.Core.Constants;
using Umbraco.Core.Dictionary;
namespace Umbraco.Web.Editors
{
@@ -48,8 +49,8 @@ namespace Umbraco.Web.Editors
[MediaControllerControllerConfiguration]
public class MediaController : ContentControllerBase
{
public MediaController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
public MediaController(ICultureDictionary cultureDictionary, PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
{
_propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
}
@@ -36,8 +36,8 @@ namespace Umbraco.Web.Editors
[MediaTypeControllerControllerConfiguration]
public class MediaTypeController : ContentTypeControllerBase<IMediaType>
{
public MediaTypeController(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(cultureDictionaryFactory, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
public MediaTypeController(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
{
}
+59 -251
View File
@@ -30,6 +30,8 @@ using Umbraco.Web.ContentApps;
using Umbraco.Web.Editors.Binders;
using Umbraco.Web.Editors.Filters;
using Constants = Umbraco.Core.Constants;
using Umbraco.Core.Dictionary;
using Umbraco.Web.Security;
namespace Umbraco.Web.Editors
{
@@ -38,27 +40,19 @@ namespace Umbraco.Web.Editors
/// access to ALL of the methods on this controller will need access to the member application.
/// </remarks>
[PluginController("UmbracoApi")]
[UmbracoApplicationAuthorizeAttribute(Constants.Applications.Members)]
[UmbracoApplicationAuthorize(Constants.Applications.Members)]
[OutgoingNoHyphenGuidFormat]
public class MemberController : ContentControllerBase
{
public MemberController(PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
public MemberController(ICultureDictionary cultureDictionary, PropertyEditorCollection propertyEditors, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
{
_propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
}
private readonly MembershipProvider _provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
private readonly MembershipProvider _provider = MembershipProviderExtensions.GetMembersMembershipProvider();
private readonly PropertyEditorCollection _propertyEditors;
/// <summary>
/// Returns the currently configured membership scenario for members in umbraco
/// </summary>
/// <value></value>
protected MembershipScenario MembershipScenario
{
get { return Services.MemberTypeService.GetMembershipScenario(); }
}
public PagedResult<MemberBasic> GetPagedResults(
int pageNumber = 1,
@@ -75,57 +69,19 @@ namespace Umbraco.Web.Editors
throw new NotSupportedException("Both pageNumber and pageSize must be greater than zero");
}
if (MembershipScenario == MembershipScenario.NativeUmbraco)
{
var members = Services.MemberService
var members = Services.MemberService
.GetAll((pageNumber - 1), pageSize, out var totalRecords, orderBy, orderDirection, orderBySystemField, memberTypeAlias, filter).ToArray();
if (totalRecords == 0)
{
return new PagedResult<MemberBasic>(0, 0, 0);
}
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize)
{
Items = members
.Select(x => Mapper.Map<MemberBasic>(x))
};
return pagedResult;
}
else
if (totalRecords == 0)
{
int totalRecords;
MembershipUserCollection members;
if (filter.IsNullOrWhiteSpace())
{
members = _provider.GetAllUsers((pageNumber - 1), pageSize, out totalRecords);
}
else
{
//we need to search!
//try by name first
members = _provider.FindUsersByName(filter, (pageNumber - 1), pageSize, out totalRecords);
if (totalRecords == 0)
{
//try by email then
members = _provider.FindUsersByEmail(filter, (pageNumber - 1), pageSize, out totalRecords);
}
}
if (totalRecords == 0)
{
return new PagedResult<MemberBasic>(0, 0, 0);
}
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize)
{
Items = members
.Cast<MembershipUser>()
.Select(Mapper.Map<MembershipUser, MemberBasic>)
};
return pagedResult;
return new PagedResult<MemberBasic>(0, 0, 0);
}
var pagedResult = new PagedResult<MemberBasic>(totalRecords, pageNumber, pageSize)
{
Items = members
.Select(x => Mapper.Map<MemberBasic>(x))
};
return pagedResult;
}
/// <summary>
@@ -165,48 +121,12 @@ namespace Umbraco.Web.Editors
[OutgoingEditorModelEvent]
public MemberDisplay GetByKey(Guid key)
{
MembershipUser foundMembershipMember;
MemberDisplay display;
IMember foundMember;
switch (MembershipScenario)
var foundMember = Services.MemberService.GetByKey(key);
if (foundMember == null)
{
case MembershipScenario.NativeUmbraco:
foundMember = Services.MemberService.GetByKey(key);
if (foundMember == null)
{
HandleContentNotFound(key);
}
return Mapper.Map<MemberDisplay>(foundMember);
case MembershipScenario.CustomProviderWithUmbracoLink:
// TODO: Support editing custom properties for members with a custom membership provider here.
//foundMember = Services.MemberService.GetByKey(key);
//if (foundMember == null)
//{
// HandleContentNotFound(key);
//}
//foundMembershipMember = Membership.GetUser(key, false);
//if (foundMembershipMember == null)
//{
// HandleContentNotFound(key);
//}
//display = Mapper.Map<MembershipUser, MemberDisplay>(foundMembershipMember);
////map the name over
//display.Name = foundMember.Name;
//return display;
case MembershipScenario.StandaloneCustomProvider:
default:
foundMembershipMember = _provider.GetUser(key, false);
if (foundMembershipMember == null)
{
HandleContentNotFound(key);
}
display = Mapper.Map<MembershipUser, MemberDisplay>(foundMembershipMember);
return display;
HandleContentNotFound(key);
}
return Mapper.Map<MemberDisplay>(foundMember);
}
/// <summary>
@@ -218,35 +138,22 @@ namespace Umbraco.Web.Editors
public MemberDisplay GetEmpty(string contentTypeAlias = null)
{
IMember emptyContent;
switch (MembershipScenario)
if (contentTypeAlias == null)
{
case MembershipScenario.NativeUmbraco:
if (contentTypeAlias == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var contentType = Services.MemberTypeService.Get(contentTypeAlias);
if (contentType == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
emptyContent = new Member(contentType);
emptyContent.AdditionalData["NewPassword"] = Membership.GeneratePassword(provider.MinRequiredPasswordLength, provider.MinRequiredNonAlphanumericCharacters);
return Mapper.Map<MemberDisplay>(emptyContent);
case MembershipScenario.CustomProviderWithUmbracoLink:
// TODO: Support editing custom properties for members with a custom membership provider here.
case MembershipScenario.StandaloneCustomProvider:
default:
//we need to return a scaffold of a 'simple' member - basically just what a membership provider can edit
emptyContent = MemberService.CreateGenericMembershipProviderMember("", "", "", "");
emptyContent.AdditionalData["NewPassword"] = Membership.GeneratePassword(Membership.MinRequiredPasswordLength, Membership.MinRequiredNonAlphanumericCharacters);
return Mapper.Map<MemberDisplay>(emptyContent);
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var contentType = Services.MemberTypeService.Get(contentTypeAlias);
if (contentType == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
emptyContent = new Member(contentType);
emptyContent.AdditionalData["NewPassword"] = Membership.GeneratePassword(provider.MinRequiredPasswordLength, provider.MinRequiredNonAlphanumericCharacters);
return Mapper.Map<MemberDisplay>(emptyContent);
}
/// <summary>
@@ -268,18 +175,6 @@ namespace Umbraco.Web.Editors
// * we have a reference to the DTO object and the persisted object
// * Permissions are valid
//This is a special case for when we're not using the umbraco membership provider - when this is the case
// we will not have a ContentTypeAlias set which means the model state will be invalid but we don't care about that
// so we'll remove that model state value
if (MembershipScenario != MembershipScenario.NativeUmbraco)
{
ModelState.Remove("ContentTypeAlias");
// TODO: We're removing this because we are not displaying it but when we support the CustomProviderWithUmbracoLink scenario
// we will be able to have a real name associated so do not remove this state once that is implemented!
ModelState.Remove("Name");
}
//map the properties to the persisted entity
MapPropertyValues(contentItem);
@@ -334,17 +229,13 @@ namespace Umbraco.Web.Editors
}
//save the IMember -
// TODO: When we support the CustomProviderWithUmbracoLink scenario, we'll need to save the custom properties for that here too
if (MembershipScenario == MembershipScenario.NativeUmbraco)
{
//save the item
//NOTE: We are setting the password to NULL - this indicates to the system to not actually save the password
// so it will not get overwritten!
contentItem.PersistedContent.RawPasswordValue = null;
//save the item
//NOTE: We are setting the password to NULL - this indicates to the system to not actually save the password
// so it will not get overwritten!
contentItem.PersistedContent.RawPasswordValue = null;
//create/save the IMember
Services.MemberService.Save(contentItem.PersistedContent);
}
//create/save the IMember
Services.MemberService.Save(contentItem.PersistedContent);
//Now let's do the role provider stuff - now that we've saved the content item (that is important since
// if we are changing the username, it must be persisted before looking up the member roles).
@@ -560,26 +451,14 @@ namespace Umbraco.Web.Editors
{
var currProps = contentItem.PersistedContent.Properties.ToArray();
switch (MembershipScenario)
switch (lookup)
{
case MembershipScenario.NativeUmbraco:
switch (lookup)
{
case LookupType.ByKey:
//Go and re-fetch the persisted item
contentItem.PersistedContent = Services.MemberService.GetByKey(contentItem.Key);
break;
case LookupType.ByUserName:
contentItem.PersistedContent = Services.MemberService.GetByUsername(contentItem.Username.Trim());
break;
}
break;
case MembershipScenario.CustomProviderWithUmbracoLink:
case MembershipScenario.StandaloneCustomProvider:
default:
var membershipUser = _provider.GetUser(contentItem.Key, false);
case LookupType.ByKey:
//Go and re-fetch the persisted item
contentItem.PersistedContent = Mapper.Map<MembershipUser, IMember>(membershipUser);
contentItem.PersistedContent = Services.MemberService.GetByKey(contentItem.Key);
break;
case LookupType.ByUserName:
contentItem.PersistedContent = Services.MemberService.GetByUsername(contentItem.Username.Trim());
break;
}
@@ -643,59 +522,16 @@ namespace Umbraco.Web.Editors
{
MembershipUser membershipUser;
switch (MembershipScenario)
{
case MembershipScenario.NativeUmbraco:
//We are using the umbraco membership provider, create the member using the membership provider first.
var umbracoMembershipProvider = (UmbracoMembershipProviderBase)_provider;
// TODO: We are not supporting q/a - passing in empty here
membershipUser = umbracoMembershipProvider.CreateUser(
contentItem.ContentTypeAlias, contentItem.Username,
contentItem.Password.NewPassword,
contentItem.Email, "", "",
contentItem.IsApproved,
Guid.NewGuid(), //since it's the umbraco provider, the user key here doesn't make any difference
out status);
break;
case MembershipScenario.CustomProviderWithUmbracoLink:
//We are using a custom membership provider, we'll create an empty IMember first to get the unique id to use
// as the provider user key.
//create it - this persisted item has already been set in the MemberBinder based on the 'Member' member type:
Services.MemberService.Save(contentItem.PersistedContent);
// TODO: We are not supporting q/a - passing in empty here
membershipUser = _provider.CreateUser(
contentItem.Username,
contentItem.Password.NewPassword,
contentItem.Email,
"TEMP", //some membership provider's require something here even if q/a is disabled!
"TEMP", //some membership provider's require something here even if q/a is disabled!
contentItem.IsApproved,
contentItem.PersistedContent.Key, //custom membership provider, we'll link that based on the IMember unique id (GUID)
out status);
break;
case MembershipScenario.StandaloneCustomProvider:
// we don't have a member type to use so we will just create the basic membership user with the provider with no
// link back to the umbraco data
var newKey = Guid.NewGuid();
// TODO: We are not supporting q/a - passing in empty here
membershipUser = _provider.CreateUser(
contentItem.Username,
contentItem.Password.NewPassword,
contentItem.Email,
"TEMP", //some membership provider's require something here even if q/a is disabled!
"TEMP", //some membership provider's require something here even if q/a is disabled!
contentItem.IsApproved,
newKey,
out status);
break;
default:
throw new ArgumentOutOfRangeException();
}
//We are using the umbraco membership provider, create the member using the membership provider first.
var umbracoMembershipProvider = (UmbracoMembershipProviderBase)_provider;
// TODO: We are not supporting q/a - passing in empty here
membershipUser = umbracoMembershipProvider.CreateUser(
contentItem.ContentTypeAlias, contentItem.Username,
contentItem.Password.NewPassword,
contentItem.Email, "", "",
contentItem.IsApproved,
Guid.NewGuid(), //since it's the umbraco provider, the user key here doesn't make any difference
out status);
// TODO: Localize these!
switch (status)
@@ -776,40 +612,12 @@ namespace Umbraco.Web.Editors
[HttpPost]
public HttpResponseMessage DeleteByKey(Guid key)
{
IMember foundMember;
MembershipUser foundMembershipUser;
switch (MembershipScenario)
var foundMember = Services.MemberService.GetByKey(key);
if (foundMember == null)
{
case MembershipScenario.NativeUmbraco:
foundMember = Services.MemberService.GetByKey(key);
if (foundMember == null)
{
return HandleContentNotFound(key, false);
}
Services.MemberService.Delete(foundMember);
break;
case MembershipScenario.CustomProviderWithUmbracoLink:
foundMember = Services.MemberService.GetByKey(key);
if (foundMember != null)
{
Services.MemberService.Delete(foundMember);
}
foundMembershipUser = _provider.GetUser(key, false);
if (foundMembershipUser != null)
{
_provider.DeleteUser(foundMembershipUser.UserName, true);
}
break;
case MembershipScenario.StandaloneCustomProvider:
foundMembershipUser = _provider.GetUser(key, false);
if (foundMembershipUser != null)
{
_provider.DeleteUser(foundMembershipUser.UserName, true);
}
break;
default:
throw new ArgumentOutOfRangeException();
return HandleContentNotFound(key, false);
}
Services.MemberService.Delete(foundMember);
return Request.CreateResponse(HttpStatusCode.OK);
}
@@ -10,6 +10,7 @@ using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.Security;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
@@ -22,8 +23,6 @@ namespace Umbraco.Web.Editors
[UmbracoTreeAuthorize(Constants.Trees.MemberGroups)]
public class MemberGroupController : UmbracoAuthorizedJsonController
{
private readonly MembershipProvider _provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
public MemberGroupDisplay GetById(int id)
{
var memberGroup = Services.MemberGroupService.GetById(id);
@@ -38,13 +37,8 @@ namespace Umbraco.Web.Editors
public IEnumerable<MemberGroupDisplay> GetByIds([FromUri]int[] ids)
{
if (_provider.IsUmbracoMembershipProvider())
{
return Services.MemberGroupService.GetByIds(ids)
return Services.MemberGroupService.GetByIds(ids)
.Select(Mapper.Map<IMemberGroup, MemberGroupDisplay>);
}
return Enumerable.Empty<MemberGroupDisplay>();
}
[HttpDelete]
@@ -63,13 +57,8 @@ namespace Umbraco.Web.Editors
public IEnumerable<MemberGroupDisplay> GetAllGroups()
{
if (_provider.IsUmbracoMembershipProvider())
{
return Services.MemberGroupService.GetAll()
return Services.MemberGroupService.GetAll()
.Select(Mapper.Map<IMemberGroup, MemberGroupDisplay>);
}
return Enumerable.Empty<MemberGroupDisplay>();
}
public MemberGroupDisplay GetEmpty()
@@ -16,6 +16,7 @@ using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.Security;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
@@ -29,13 +30,11 @@ namespace Umbraco.Web.Editors
[UmbracoTreeAuthorize(new string[] { Constants.Trees.MemberTypes, Constants.Trees.Members})]
public class MemberTypeController : ContentTypeControllerBase<IMemberType>
{
public MemberTypeController(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(cultureDictionaryFactory, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
public MemberTypeController(ICultureDictionary cultureDictionary, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
: base(cultureDictionary, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
{
}
private readonly MembershipProvider _provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider();
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
public MemberTypeDisplay GetById(int id)
{
@@ -114,12 +113,8 @@ namespace Umbraco.Web.Editors
/// </summary>
public IEnumerable<ContentTypeBasic> GetAllTypes()
{
if (_provider.IsUmbracoMembershipProvider())
{
return Services.MemberTypeService.GetAll()
return Services.MemberTypeService.GetAll()
.Select(Mapper.Map<IMemberType, ContentTypeBasic>);
}
return Enumerable.Empty<ContentTypeBasic>();
}
[UmbracoTreeAuthorize(Constants.Trees.MemberTypes)]
+16 -36
View File
@@ -2,7 +2,6 @@
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http.ModelBinding;
using System.Web.Security;
using Umbraco.Core;
using Umbraco.Core.Logging;
@@ -46,27 +45,6 @@ namespace Umbraco.Web.Editors
if (passwordModel == null) throw new ArgumentNullException(nameof(passwordModel));
if (userMgr == null) throw new ArgumentNullException(nameof(userMgr));
//check if this identity implementation is powered by an underlying membership provider (it will be in most cases)
var membershipPasswordHasher = userMgr.PasswordHasher as IMembershipProviderPasswordHasher;
//check if this identity implementation is powered by an IUserAwarePasswordHasher (it will be by default in 7.7+ but not for upgrades)
if (membershipPasswordHasher != null && !(userMgr.PasswordHasher is IUserAwarePasswordHasher<BackOfficeIdentityUser, int>))
{
//if this isn't using an IUserAwarePasswordHasher, then fallback to the old way
if (membershipPasswordHasher.MembershipProvider.RequiresQuestionAndAnswer)
throw new NotSupportedException("Currently the user editor does not support providers that have RequiresQuestionAndAnswer specified");
return ChangePasswordWithMembershipProvider(savingUser.Username, passwordModel, membershipPasswordHasher.MembershipProvider);
}
//if we are here, then a IUserAwarePasswordHasher is available, however we cannot proceed in that case if for some odd reason
//the user has configured the membership provider to not be hashed. This will actually never occur because the BackOfficeUserManager
//will throw if it's not hashed, but we should make sure to check anyways (i.e. in case we want to unit test!)
if (membershipPasswordHasher != null && membershipPasswordHasher.MembershipProvider.PasswordFormat != MembershipPasswordFormat.Hashed)
{
throw new InvalidOperationException("The membership provider cannot have a password format of " + membershipPasswordHasher.MembershipProvider.PasswordFormat + " and be configured with secured hashed passwords");
}
//Are we resetting the password?
//This flag indicates that either an admin user is changing another user's password without knowing the original password
// or that the password needs to be reset to an auto-generated one.
@@ -125,7 +103,7 @@ namespace Umbraco.Web.Editors
}
//is the old password correct?
var validateResult = await userMgr.CheckPasswordAsync(backOfficeIdentityUser, passwordModel.OldPassword);
if(validateResult == false)
if (validateResult == false)
{
//no, fail with an error message for "oldPassword"
return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Incorrect password", new[] { "oldPassword" }) });
@@ -149,7 +127,10 @@ namespace Umbraco.Web.Editors
/// <param name="passwordModel"></param>
/// <param name="membershipProvider"></param>
/// <returns></returns>
public Attempt<PasswordChangedModel> ChangePasswordWithMembershipProvider(string username, ChangingPasswordModel passwordModel, MembershipProvider membershipProvider)
public Attempt<PasswordChangedModel> ChangePasswordWithMembershipProvider(
string username,
ChangingPasswordModel passwordModel,
MembershipProvider membershipProvider)
{
var umbracoBaseProvider = membershipProvider as MembershipProviderBase;
@@ -158,20 +139,19 @@ namespace Umbraco.Web.Editors
if (passwordModel == null) throw new ArgumentNullException(nameof(passwordModel));
if (membershipProvider == null) throw new ArgumentNullException(nameof(membershipProvider));
BackOfficeUserManager<BackOfficeIdentityUser> backofficeUserManager = null;
var backofficeUserManager = _httpContext.GetOwinContext().GetBackOfficeUserManager();
var userId = -1;
if (membershipProvider.IsUmbracoUsersProvider())
if (backofficeUserManager == null)
{
backofficeUserManager = _httpContext.GetOwinContext().GetBackOfficeUserManager();
if (backofficeUserManager != null)
{
var profile = _userService.GetProfileByUserName(username);
if (profile != null)
int.TryParse(profile.Id.ToString(), out userId);
}
// should never happen
throw new InvalidOperationException("Could not resolve the back office user manager");
}
var profile = _userService.GetProfileByUserName(username);
if (profile != null)
int.TryParse(profile.Id.ToString(), out userId);
//Are we resetting the password?
//This flag indicates that either an admin user is changing another user's password without knowing the original password
// or that the password needs to be reset to an auto-generated one.
@@ -181,7 +161,7 @@ namespace Umbraco.Web.Editors
//this is only possible when using a membership provider if the membership provider supports AllowManuallyChangingPassword
if (passwordModel.NewPassword.IsNullOrWhiteSpace() == false)
{
if (umbracoBaseProvider !=null && umbracoBaseProvider.AllowManuallyChangingPassword)
if (umbracoBaseProvider != null && umbracoBaseProvider.AllowManuallyChangingPassword)
{
//this provider allows manually changing the password without the old password, so we can just do it
try
@@ -209,7 +189,7 @@ namespace Umbraco.Web.Editors
//we've made it here which means we need to generate a new password
var canReset = membershipProvider.CanResetPassword(_userService);
var canReset = true; // TODO: Remove this! This is legacy, all of this will be gone when we get membership providers entirely gone
if (canReset == false)
{
return Attempt.Fail(new PasswordChangedModel { ChangeError = new ValidationResult("Password reset is not enabled", new[] { "resetPassword" }) });
@@ -226,7 +206,7 @@ namespace Umbraco.Web.Editors
username,
membershipProvider.RequiresQuestionAndAnswer ? passwordModel.Answer : null);
if (membershipProvider.IsUmbracoUsersProvider() && backofficeUserManager != null && userId >= 0)
if (userId >= 0)
backofficeUserManager.RaisePasswordResetEvent(userId);
//return the generated pword

Some files were not shown because too many files have changed in this diff Show More