Merge remote-tracking branch 'origin/netcore/netcore' into netcore/task/6973-migrating-authenticationcontroller

Signed-off-by: Bjarke Berg <mail@bergmania.dk>

# Conflicts:
#	src/Umbraco.Infrastructure/HealthCheck/NotificationMethods/EmailNotificationMethod.cs
#	src/Umbraco.Web/Editors/AuthenticationController.cs
This commit is contained in:
Bjarke Berg
2020-10-26 14:30:59 +01:00
319 changed files with 2623 additions and 5551 deletions

View File

@@ -41,7 +41,7 @@ namespace Umbraco.Core.Cache
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
{
var plen = CacheItemPrefix.Length + 1;
IEnumerable<DictionaryEntry> entries;
IEnumerable<KeyValuePair<object, object>> entries;
try
{
EnterReadLock();
@@ -65,7 +65,7 @@ namespace Umbraco.Core.Cache
const string prefix = CacheItemPrefix + "-";
var compiled = new Regex(regex, RegexOptions.Compiled);
var plen = prefix.Length;
IEnumerable<DictionaryEntry> entries;
IEnumerable<KeyValuePair<object, object>> entries;
try
{
EnterReadLock();
@@ -249,7 +249,7 @@ namespace Umbraco.Core.Cache
// manipulate the underlying cache entries
// these *must* be called from within the appropriate locks
// and use the full prefixed cache keys
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
protected abstract IEnumerable<KeyValuePair<object, object>> GetDictionaryEntries();
protected abstract void RemoveEntry(string key);
protected abstract object GetEntry(string key);

View File

@@ -115,13 +115,13 @@ namespace Umbraco.Core.Cache
#region Entries
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
protected override IEnumerable<KeyValuePair<object, object>> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
if (!TryGetContextItems(out var items)) return Enumerable.Empty<DictionaryEntry>();
if (!TryGetContextItems(out var items)) return Enumerable.Empty<KeyValuePair<object, object>>();
return items.Cast<DictionaryEntry>()
return items.Cast<KeyValuePair<object, object>>()
.Where(x => x.Key is string s && s.StartsWith(prefix));
}

View File

@@ -3,7 +3,6 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
@@ -17,19 +16,23 @@ namespace Umbraco.Core.Cache
/// </remarks>
public class HttpRequestAppCache : FastDictionaryAppCacheBase, IRequestCache
{
private static object _syncRoot = new object(); // Using this for locking as the SyncRoot property is not available to us
// on the provided collection provided from .NET Core's HttpContext.Items dictionary,
// as it doesn't implement ICollection where SyncRoot is defined.
/// <summary>
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class with a context, for unit tests!
/// </summary>
public HttpRequestAppCache(Func<IDictionary> requestItems) : base()
public HttpRequestAppCache(Func<IDictionary<object, object>> requestItems) : base()
{
ContextItems = requestItems;
}
private Func<IDictionary> ContextItems { get; }
private Func<IDictionary<object, object>> ContextItems { get; }
public bool IsAvailable => TryGetContextItems(out _);
private bool TryGetContextItems(out IDictionary items)
private bool TryGetContextItems(out IDictionary<object, object> items)
{
items = ContextItems?.Invoke();
return items != null;
@@ -115,13 +118,13 @@ namespace Umbraco.Core.Cache
#region Entries
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
protected override IEnumerable<KeyValuePair<object, object>> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
if (!TryGetContextItems(out var items)) return Enumerable.Empty<DictionaryEntry>();
if (!TryGetContextItems(out var items)) return Enumerable.Empty<KeyValuePair<object, object>>();
return items.Cast<DictionaryEntry>()
return items.Cast<KeyValuePair<object, object>>()
.Where(x => x.Key is string s && s.StartsWith(prefix));
}
@@ -154,7 +157,7 @@ namespace Umbraco.Core.Cache
// ContextItems - which is locked, so this should be safe
var entered = false;
Monitor.Enter(items.SyncRoot, ref entered);
Monitor.Enter(_syncRoot, ref entered);
items[ContextItemsLockKey] = entered;
}
@@ -166,7 +169,7 @@ namespace Umbraco.Core.Cache
var entered = (bool?)items[ContextItemsLockKey] ?? false;
if (entered)
Monitor.Exit(items.SyncRoot);
Monitor.Exit(_syncRoot);
items.Remove(ContextItemsLockKey);
}
@@ -179,7 +182,7 @@ namespace Umbraco.Core.Cache
yield break;
}
foreach (DictionaryEntry item in items)
foreach (var item in items)
{
yield return new KeyValuePair<string, object>(item.Key.ToString(), item.Value);
}

View File

@@ -64,10 +64,7 @@ namespace Umbraco.Core.Composing
#endregion
#region IRegister
/// <inheritdoc />
public object Concrete => _register.Concrete;
/// <inheritdoc />
public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient)
=> _register.Register(serviceType, lifetime);
@@ -85,33 +82,6 @@ namespace Umbraco.Core.Composing
public void Register(Type serviceType, object instance)
=> _register.Register(serviceType, instance);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
where TService : class
=> _register.RegisterFor<TService, TTarget>(lifetime);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(Type implementingType, Lifetime lifetime = Lifetime.Transient)
where TService : class
=> _register.RegisterFor<TService, TTarget>(implementingType, lifetime);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
where TService : class
=> _register.RegisterFor<TService, TTarget>(factory, lifetime);
/// <inheritdoc />
public void RegisterFor<TService, TTarget>(TService instance)
where TService : class
=> _register.RegisterFor<TService, TTarget>(instance);
/// <inheritdoc />
public void RegisterAuto(Type serviceBaseType)
=> _register.RegisterAuto(serviceBaseType);
/// <inheritdoc />
public void ConfigureForWeb()
=> _register.ConfigureForWeb();
/// <inheritdoc />
public IFactory CreateFactory()
@@ -127,13 +97,7 @@ namespace Umbraco.Core.Composing
builder.RegisterWith(_register);
_builders.Clear(); // no point keep them around
IFactory factory = null;
// ReSharper disable once AccessToModifiedClosure -- on purpose
_register.Register(_ => factory, Lifetime.Singleton);
factory = _register.CreateFactory();
return factory;
return _register.CreateFactory();
}
/// <summary>
@@ -186,38 +150,6 @@ namespace Umbraco.Core.Composing
public void RegisterUnique(Type serviceType, object instance)
=> _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, instance);
/// <summary>
/// Registers a unique service for a target, as its own implementation.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>()
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(Lifetime.Singleton);
/// <summary>
/// Registers a unique service for a target, with an implementing type.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>(Type implementingType)
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(implementingType, Lifetime.Singleton);
/// <summary>
/// Registers a unique service for a target, with an implementation factory.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>(Func<IFactory, TService> factory)
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(factory, Lifetime.Singleton);
/// <summary>
/// Registers a unique service for a target, with an implementing instance.
/// </summary>
/// <remarks>Unique services have one single implementation, and a Singleton lifetime.</remarks>
public void RegisterUniqueFor<TService, TTarget>(TService instance)
where TService : class
=> _uniques[GetUniqueName<TService, TTarget>()] = register => register.RegisterFor<TService, TTarget>(instance);
#endregion
#region Collection Builders

View File

@@ -21,15 +21,6 @@ namespace Umbraco.Core.Composing
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
object GetInstance(Type type);
/// <summary>
/// Gets a targeted instance of a service.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <typeparam name="TTarget">The type of the target.</typeparam>
/// <returns>The instance of the specified type for the specified target.</returns>
/// <remarks>Throws an exception if the container failed to get an instance of the specified type.</remarks>
TService GetInstanceFor<TService, TTarget>();
/// <summary>
/// Tries to get an instance of a service.
/// </summary>
@@ -52,18 +43,7 @@ namespace Umbraco.Core.Composing
/// <typeparam name="TService">The type of the service.</typeparam>
IEnumerable<TService> GetAllInstances<TService>()
where TService : class;
/// <summary>
/// Releases an instance.
/// </summary>
/// <param name="instance">The instance.</param>
/// <remarks>
/// See https://stackoverflow.com/questions/14072208 and http://kozmic.net/2010/08/27/must-i-release-everything-when-using-windsor/,
/// you only need to release instances you specifically resolved, and even then, if done right, that might never be needed. For
/// instance, LightInject does not require this and does not support it - should work with scopes.
/// </remarks>
void Release(object instance);
/// <summary>
/// Begins a scope.
/// </summary>
@@ -72,13 +52,5 @@ namespace Umbraco.Core.Composing
/// <para>Scopes can be nested. Each instance is disposed individually.</para>
/// </remarks>
IDisposable BeginScope();
/// <summary>
/// Enables per-request scope.
/// </summary>
/// <remarks>
/// <para>Ties scopes to web requests.</para>
/// </remarks>
void EnablePerWebRequestScope();
}
}

View File

@@ -7,11 +7,6 @@ namespace Umbraco.Core.Composing
/// </summary>
public interface IRegister
{
/// <summary>
/// Gets the concrete container.
/// </summary>
object Concrete { get; }
/// <summary>
/// Registers a service as its own implementation.
/// </summary>
@@ -33,69 +28,8 @@ namespace Umbraco.Core.Composing
/// </summary>
void Register(Type serviceType, object instance);
/// <summary>
/// Registers a service for a target, as its own implementation.
/// </summary>
/// <remarks>
/// There can only be one implementation or instanced registered for a service and target;
/// what happens if many are registered is not specified.
/// </remarks>
void RegisterFor<TService, TTarget>(Lifetime lifetime = Lifetime.Transient)
where TService : class;
/// <summary>
/// Registers a service for a target, with an implementation type.
/// </summary>
/// <remarks>
/// There can only be one implementation or instanced registered for a service and target;
/// what happens if many are registered is not specified.
/// </remarks>
void RegisterFor<TService, TTarget>(Type implementingType, Lifetime lifetime = Lifetime.Transient)
where TService : class;
/// <summary>
/// Registers a service for a target, with an implementation factory.
/// </summary>
/// <remarks>
/// There can only be one implementation or instanced registered for a service and target;
/// what happens if many are registered is not specified.
/// </remarks>
void RegisterFor<TService, TTarget>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
where TService : class;
/// <summary>
/// Registers a service for a target, with an implementing instance.
/// </summary>
/// <remarks>
/// There can only be one implementation or instanced registered for a service and target;
/// what happens if many are registered is not specified.
/// </remarks>
void RegisterFor<TService, TTarget>(TService instance)
where TService : class;
/// <summary>
/// Registers a base type for auto-registration.
/// </summary>
/// <remarks>
/// <para>Auto-registration means that anytime the container is asked to create an instance
/// of a type deriving from <paramref name="serviceBaseType"/>, it will first register that
/// type automatically.</para>
/// <para>This can be used for instance for views or controllers. Then, one just needs to
/// register a common base class or interface, and the container knows how to create instances.</para>
/// </remarks>
void RegisterAuto(Type serviceBaseType);
#region Control
/// <summary>
/// Configures the container for web support.
/// </summary>
/// <remarks>
/// <para>Enables support for MVC, WebAPI, but *not* per-request scope. This is used early in the boot
/// process, where anything "scoped" should not be linked to a web request.</para>
/// </remarks>
void ConfigureForWeb(); // TODO: Unsure if we need this anymore
/// <summary>
/// Creates the factory.
/// </summary>

View File

@@ -1,18 +0,0 @@
namespace Umbraco.Core.Composing
{
/// <summary>
/// Provides a base class for targeted service factories.
/// </summary>
/// <typeparam name="TService"></typeparam>
public abstract class TargetedServiceFactory<TService>
{
private readonly IFactory _factory;
protected TargetedServiceFactory(IFactory factory)
{
_factory = factory;
}
public TService For<TTarget>() => _factory.GetInstanceFor<TService, TTarget>();
}
}

View File

@@ -19,13 +19,6 @@ namespace Umbraco.Core
public static void RegisterUnique<TService, TImplementing>(this Composition composition)
=> composition.RegisterUnique(typeof(TService), typeof(TImplementing));
/// <summary>
/// Registers a unique service with an implementation type, for a target.
/// </summary>
public static void RegisterUniqueFor<TService, TTarget, TImplementing>(this Composition composition)
where TService : class
=> composition.RegisterUniqueFor<TService, TTarget>(typeof(TImplementing));
/// <summary>
/// Registers a unique service with an implementing instance.
/// </summary>

View File

@@ -1,13 +1,13 @@
using System;
using System.Net.Mail;
using Umbraco.Core.Models;
namespace Umbraco.Core.Events
{
public class SendEmailEventArgs : EventArgs
{
public MailMessage Message { get; private set; }
public EmailMessage Message { get; }
public SendEmailEventArgs(MailMessage message)
public SendEmailEventArgs(EmailMessage message)
{
Message = message;
}

View File

@@ -1,5 +1,5 @@
using System.Net.Mail;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Umbraco.Core.Models;
namespace Umbraco.Core
{
@@ -8,7 +8,6 @@ namespace Umbraco.Core
/// </summary>
public interface IEmailSender
{
// TODO: This would be better if MailMessage was our own abstraction!
Task SendAsync(MailMessage message);
Task SendAsync(EmailMessage message);
}
}

View File

@@ -282,7 +282,7 @@ namespace Umbraco.Core.IO
{
lock (_shadowLocker)
{
var wrapper = new ShadowWrapper(filesystem, _ioHelper, _hostingEnvironment, _loggerFactory, shadowPath, IsScoped);
var wrapper = new ShadowWrapper(filesystem, _ioHelper, _hostingEnvironment, _loggerFactory, shadowPath,() => IsScoped());
if (_shadowCurrentId != null)
wrapper.Shadow(_shadowCurrentId);
_shadowWrappers.Add(wrapper);

View File

@@ -122,7 +122,7 @@ namespace Umbraco.Core.IO
var mappedRoot = MapPath(_hostingEnvironment.ApplicationVirtualPath);
if (filePath.StartsWith(mappedRoot) == false)
filePath = MapPath(filePath);
filePath = _hostingEnvironment.MapPathContentRoot(filePath);
// yes we can (see above)
//// don't trust what we get, it may contain relative segments
@@ -132,7 +132,7 @@ namespace Umbraco.Core.IO
{
var validDir = dir;
if (validDir.StartsWith(mappedRoot) == false)
validDir = MapPath(validDir);
validDir = _hostingEnvironment.MapPathContentRoot(validDir);
if (PathStartsWith(filePath, validDir, Path.DirectorySeparatorChar))
return true;

View File

@@ -10,7 +10,8 @@ using Umbraco.Core.Hosting;
namespace Umbraco.Core.IO
{
public class PhysicalFileSystem : IFileSystem
public interface IPhysicalFileSystem : IFileSystem {}
public class PhysicalFileSystem : IPhysicalFileSystem
{
private readonly IIOHelper _ioHelper;
private readonly ILogger<PhysicalFileSystem> _logger;
@@ -56,7 +57,6 @@ namespace Umbraco.Core.IO
if (string.IsNullOrEmpty(rootUrl)) throw new ArgumentException("Value can't be empty.", nameof(rootUrl));
if (rootPath.StartsWith("~/")) throw new ArgumentException("Value can't be a virtual path and start with '~/'.", nameof(rootPath));
// rootPath should be... rooted, as in, it's a root path!
if (Path.IsPathRooted(rootPath) == false)
{
@@ -65,6 +65,9 @@ namespace Umbraco.Core.IO
rootPath = Path.Combine(localRoot, rootPath);
}
// clean up root path
rootPath = Path.GetFullPath(rootPath);
_rootPath = EnsureDirectorySeparatorChar(rootPath).TrimEnd(Path.DirectorySeparatorChar);
_rootPathFwd = EnsureUrlSeparatorChar(_rootPath);
_rootUrl = EnsureUrlSeparatorChar(rootUrl).TrimEnd('/');
@@ -328,7 +331,7 @@ namespace Umbraco.Core.IO
// nothing prevents us to reach the file, security-wise, yet it is outside
// this filesystem's root - throw
throw new UnauthorizedAccessException("File '" + opath + "' is outside this filesystem's root.");
throw new UnauthorizedAccessException($"File original: [{opath}] full: [{path}] is outside this filesystem's root.");
}
/// <summary>

View File

@@ -1,11 +0,0 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.IO
{
public class SupportingFileSystems : TargetedServiceFactory<IFileSystem>
{
public SupportingFileSystems(IFactory factory)
: base(factory)
{ }
}
}

View File

@@ -0,0 +1,35 @@
using System;
namespace Umbraco.Core.Models
{
public class EmailMessage
{
public string From { get; }
public string To { get; }
public string Subject { get; }
public string Body { get; }
public bool IsBodyHtml { get; }
public EmailMessage(string from, string to, string subject, string body, bool isBodyHtml)
{
if (from == null) throw new ArgumentNullException(nameof(from));
if (from.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(from));
if (to == null) throw new ArgumentNullException(nameof(to));
if (to.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(to));
if (subject == null) throw new ArgumentNullException(nameof(subject));
if (subject.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(subject));
if (body == null) throw new ArgumentNullException(nameof(body));
if (body.Length == 0) throw new ArgumentException("Value cannot be empty.", nameof(body));
From = from;
To = to;
Subject = subject;
Body = body;
IsBodyHtml = isBodyHtml;
}
}
}

View File

@@ -13,13 +13,6 @@ namespace Umbraco.Core
public static void Register<TService, TImplementing>(this IRegister register, Lifetime lifetime = Lifetime.Transient)
=> register.Register(typeof(TService), typeof(TImplementing), lifetime);
/// <summary>
/// Registers a service with an implementation type, for a target.
/// </summary>
public static void RegisterFor<TService, TImplementing, TTarget>(this IRegister register, Lifetime lifetime = Lifetime.Transient)
where TService : class
=> register.RegisterFor<TService, TTarget>(typeof(TImplementing), lifetime);
/// <summary>
/// Registers a service as its own implementation.
/// </summary>
@@ -33,12 +26,5 @@ namespace Umbraco.Core
public static void Register<TService>(this IRegister register, TService instance)
where TService : class
=> register.Register(typeof(TService), instance);
/// <summary>
/// Registers a base type for auto-registration.
/// </summary>
public static void RegisterAuto<TServiceBase>(this IRegister register)
where TServiceBase : class
=> register.RegisterAuto(typeof(TServiceBase));
}
}