Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forward merge v11/dev to v12/dev #13509

Merged
merged 22 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1a0983f
update marketplace url to its final, live url
iOvergaard Nov 28, 2022
493e93c
Added various Block Grid translations (EN & NL) (#13488)
cornehoskam Nov 28, 2022
e45ed3d
Revert breaking changes adding IContextCache to deploy connectors (#1…
ronaldbarendse Nov 28, 2022
a336160
Create and pack empty file to add TFM dependency (#13475)
ronaldbarendse Nov 28, 2022
ed67cd8
Change login image to new SVG. (#13493)
nielslyngsoe Nov 28, 2022
8c483e8
Only add global usings when ImplicitUsings is enabled (#13491)
ronaldbarendse Nov 28, 2022
082864f
bump to rc6
bergmania Nov 28, 2022
65f66c2
Merge remote-tracking branch 'origin/release/11.0' into release/11.0
bergmania Nov 28, 2022
bfe9579
V11: Allow changing logging directory from configuration (#13485)
bergmania Nov 29, 2022
a76d68b
Merge remote-tracking branch 'origin/release/11.0' into v11/dev
bergmania Nov 29, 2022
9bed0cd
Move login.svg file
bergmania Nov 29, 2022
b9990f2
Make sure contexbar fits within the available space (#13467)
nielslyngsoe Nov 29, 2022
b5a3dcb
Info panes not loading prerequisites (#13486)
iOvergaard Nov 29, 2022
abd490f
Merge remote-tracking branch 'origin/release/11.0' into release/11.0
bergmania Nov 29, 2022
acbaaec
do not set layout gaps (#13496)
nielslyngsoe Nov 29, 2022
6c13c87
Nuget updates (#13478)
bergmania Nov 29, 2022
18fd0c9
Nuget updates (#13478)
bergmania Nov 29, 2022
d713b38
Bump version to final
nikolajlauridsen Nov 29, 2022
934bb8b
Merge tag 'release-11.0.0' into v11/dev
nikolajlauridsen Dec 1, 2022
2ce2059
V11/cherry pick flaky test (#13506)
andr317c Dec 1, 2022
2143d8c
Handle external authentication providers (#13487)
kjac Dec 1, 2022
d698399
Merge branch 'v11/dev' into v12/fix/merge-with-v11-dev-take-2
kjac Dec 1, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Web.BackOffice.Security;
using Umbraco.Extensions;
using Umbraco.New.Cms.Web.Common.Routing;
using SignInResult = Microsoft.AspNetCore.Mvc.SignInResult;
using IdentitySignInResult = Microsoft.AspNetCore.Identity.SignInResult;

namespace Umbraco.Cms.ManagementApi.Controllers.Security;

Expand All @@ -21,12 +26,18 @@ public class BackOfficeController : ManagementApiControllerBase
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IBackOfficeSignInManager _backOfficeSignInManager;
private readonly IBackOfficeUserManager _backOfficeUserManager;
private readonly IOptions<SecuritySettings> _securitySettings;

public BackOfficeController(IHttpContextAccessor httpContextAccessor, IBackOfficeSignInManager backOfficeSignInManager, IBackOfficeUserManager backOfficeUserManager)
public BackOfficeController(
IHttpContextAccessor httpContextAccessor,
IBackOfficeSignInManager backOfficeSignInManager,
IBackOfficeUserManager backOfficeUserManager,
IOptions<SecuritySettings> securitySettings)
{
_httpContextAccessor = httpContextAccessor;
_backOfficeSignInManager = backOfficeSignInManager;
_backOfficeUserManager = backOfficeUserManager;
_securitySettings = securitySettings;
}

[HttpGet("authorize")]
Expand All @@ -41,36 +52,93 @@ public async Task<IActionResult> Authorize()
return BadRequest("Unable to obtain OpenID data from the current request");
}

return request.IdentityProvider.IsNullOrWhiteSpace()
? await AuthorizeInternal(request)
: await AuthorizeExternal(request);
}

private async Task<IActionResult> AuthorizeInternal(OpenIddictRequest request)
{
// TODO: ensure we handle sign-in notifications for internal logins.
// when the new login screen is implemented for internal logins, make sure it still handles
// user sign-in notifications (calls BackOfficeSignInManager.HandleSignIn) as part of the
// sign-in process
// for future reference, notifications are already handled for the external login flow by
// by calling BackOfficeSignInManager.ExternalLoginSignInAsync

// retrieve the user principal stored in the authentication cookie.
AuthenticateResult cookieAuthResult = await HttpContext.AuthenticateAsync(Constants.Security.BackOfficeAuthenticationType);
if (cookieAuthResult.Succeeded && cookieAuthResult.Principal?.Identity?.Name != null)
var userName = cookieAuthResult.Succeeded
? cookieAuthResult.Principal?.Identity?.Name
: null;

if (userName != null)
{
BackOfficeIdentityUser? backOfficeUser = await _backOfficeUserManager.FindByNameAsync(cookieAuthResult.Principal.Identity.Name);
BackOfficeIdentityUser? backOfficeUser = await _backOfficeUserManager.FindByNameAsync(userName);
if (backOfficeUser != null)
{
ClaimsPrincipal backOfficePrincipal = await _backOfficeSignInManager.CreateUserPrincipalAsync(backOfficeUser);
backOfficePrincipal.SetClaim(OpenIddictConstants.Claims.Subject, backOfficeUser.Key.ToString());

// TODO: it is not optimal to append all claims to the token.
// the token size grows with each claim, although it is still smaller than the old cookie.
// see if we can find a better way so we do not risk leaking sensitive data in bearer tokens.
// maybe work with scopes instead?
Claim[] backOfficeClaims = backOfficePrincipal.Claims.ToArray();
foreach (Claim backOfficeClaim in backOfficeClaims)
{
backOfficeClaim.SetDestinations(OpenIddictConstants.Destinations.AccessToken);
}
return await SignInBackOfficeUser(backOfficeUser, request);
}
}

return DefaultChallengeResult();
}

private async Task<IActionResult> AuthorizeExternal(OpenIddictRequest request)
{
var provider = request.IdentityProvider ?? throw new ArgumentException("No identity provider found in request", nameof(request));

ExternalLoginInfo? loginInfo = await _backOfficeSignInManager.GetExternalLoginInfoAsync();
if (loginInfo?.Principal != null)
{
IdentitySignInResult result = await _backOfficeSignInManager.ExternalLoginSignInAsync(loginInfo, false, _securitySettings.Value.UserBypassTwoFactorForExternalLogins);

if (request.GetScopes().Contains(OpenIddictConstants.Scopes.OfflineAccess))
if (result.Succeeded)
{
// Update any authentication tokens if succeeded
await _backOfficeSignInManager.UpdateExternalAuthenticationTokensAsync(loginInfo);

// sign in the backoffice user associated with the login provider and unique provider key
BackOfficeIdentityUser? backOfficeUser = await _backOfficeUserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey);
if (backOfficeUser != null)
{
// "offline_access" scope is required to use refresh tokens
backOfficePrincipal.SetScopes(OpenIddictConstants.Scopes.OfflineAccess);
return await SignInBackOfficeUser(backOfficeUser, request);
}

return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, backOfficePrincipal);
}
else
{
// avoid infinite auth loops when something fails by performing the default challenge (default login screen)
return DefaultChallengeResult();
}
}

return new ChallengeResult(new[] { Constants.Security.BackOfficeAuthenticationType });
AuthenticationProperties properties = _backOfficeSignInManager.ConfigureExternalAuthenticationProperties(provider, null);
return new ChallengeResult(provider, properties);
}

private async Task<IActionResult> SignInBackOfficeUser(BackOfficeIdentityUser backOfficeUser, OpenIddictRequest request)
{
ClaimsPrincipal backOfficePrincipal = await _backOfficeSignInManager.CreateUserPrincipalAsync(backOfficeUser);
backOfficePrincipal.SetClaim(OpenIddictConstants.Claims.Subject, backOfficeUser.Key.ToString());

// TODO: it is not optimal to append all claims to the token.
// the token size grows with each claim, although it is still smaller than the old cookie.
// see if we can find a better way so we do not risk leaking sensitive data in bearer tokens.
// maybe work with scopes instead?
Claim[] backOfficeClaims = backOfficePrincipal.Claims.ToArray();
foreach (Claim backOfficeClaim in backOfficeClaims)
{
backOfficeClaim.SetDestinations(OpenIddictConstants.Destinations.AccessToken);
}

if (request.GetScopes().Contains(OpenIddictConstants.Scopes.OfflineAccess))
{
// "offline_access" scope is required to use refresh tokens
backOfficePrincipal.SetScopes(OpenIddictConstants.Scopes.OfflineAccess);
}

return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, backOfficePrincipal);
}

private static IActionResult DefaultChallengeResult() => new ChallengeResult(Constants.Security.BackOfficeAuthenticationType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<ItemGroup>
<PackageReference Include="NPoco.SqlServer" Version="5.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Umbraco.Infrastructure\Umbraco.Infrastructure.csproj" />
</ItemGroup>
Expand Down
8 changes: 8 additions & 0 deletions src/Umbraco.Cms.Targets/Umbraco.Cms.Targets.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,12 @@
<Target Name="RemoveAppsettingsSchema" AfterTargets="Clean" Condition="Exists('$(_UmbracoCmsJsonSchemaReference)')">
<Delete Files="$(_UmbracoCmsJsonSchemaReference)" />
</Target>

<!-- Create and pack empty file to add TFM dependency -->
<Target Name="PackTargetFrameworkFile" BeforeTargets="Build">
<WriteLinesToFile File="$(IntermediateOutputPath)_._" />
<ItemGroup>
<None Include="$(IntermediateOutputPath)_._" Pack="true" PackagePath="lib\$(TargetFramework)" />
</ItemGroup>
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<DefaultItemExcludes>$(DefaultItemExcludes);umbraco\Logs\**</DefaultItemExcludes>
<DefaultItemExcludes>$(DefaultItemExcludes);wwwroot\media\**</DefaultItemExcludes>
</PropertyGroup>
<ItemGroup>
<ItemGroup Condition="'$(ImplicitUsings)' == 'enable' or '$(ImplicitUsings)' == 'true'">
<Using Include="Umbraco.Cms.Core.DependencyInjection" />
<Using Include="Umbraco.Extensions" />
</ItemGroup>
Expand Down
10 changes: 9 additions & 1 deletion src/Umbraco.Cms/Umbraco.Cms.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Title>Umbraco CMS</Title>
<Description>Installs Umbraco CMS with all default dependencies in your ASP.NET Core project.</Description>
Expand All @@ -12,4 +12,12 @@
<ProjectReference Include="..\Umbraco.Cms.Persistence.Sqlite\Umbraco.Cms.Persistence.Sqlite.csproj" />
<ProjectReference Include="..\Umbraco.Cms.Persistence.SqlServer\Umbraco.Cms.Persistence.SqlServer.csproj" />
</ItemGroup>

<!-- Create and pack empty file to add TFM dependency -->
<Target Name="PackTargetFrameworkFile" BeforeTargets="Build">
<WriteLinesToFile File="$(IntermediateOutputPath)_._" />
<ItemGroup>
<None Include="$(IntermediateOutputPath)_._" Pack="true" PackagePath="lib\$(TargetFramework)" />
</ItemGroup>
</Target>
</Project>
20 changes: 20 additions & 0 deletions src/Umbraco.Core/Configuration/Models/LoggingSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// See LICENSE for more details.

using System.ComponentModel;
using Microsoft.Extensions.Hosting;
using Umbraco.Cms.Core.Extensions;

namespace Umbraco.Cms.Core.Configuration.Models;

Expand All @@ -12,10 +14,28 @@ namespace Umbraco.Cms.Core.Configuration.Models;
public class LoggingSettings
{
internal const string StaticMaxLogAge = "1.00:00:00"; // TimeSpan.FromHours(24);
internal const string StaticDirectory = Constants.SystemDirectories.LogFiles;

/// <summary>
/// Gets or sets a value for the maximum age of a log file.
/// </summary>
[DefaultValue(StaticMaxLogAge)]
public TimeSpan MaxLogAge { get; set; } = TimeSpan.Parse(StaticMaxLogAge);

/// <summary>
/// Gets or sets the folder to use for log files
/// </summary>
[DefaultValue(StaticDirectory)]
public string Directory { get; set; } = StaticDirectory;

public string GetAbsoluteLoggingPath(IHostEnvironment hostEnvironment)
{
var dir = Directory;
if (dir.StartsWith("~/"))
{
return hostEnvironment.MapPathContentRoot(dir);
}

return dir;
}
}
2 changes: 1 addition & 1 deletion src/Umbraco.Core/Constants-Marketplace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ public static partial class Constants
/// </summary>
public static class Marketplace
{
public const string Url = "https://dev.marketplace.umbraco.com";
public const string Url = "https://marketplace.umbraco.com";
}
}
1 change: 1 addition & 0 deletions src/Umbraco.Core/Constants-SystemDirectories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static class SystemDirectories
/// <summary>
/// The default folder where Umbraco log files are stored
/// </summary>
[Obsolete("Use LoggingSettings.GetLoggingDirectory() instead, will be removed in Umbraco 13.")]
public const string LogFiles = Umbraco + "/Logs";

[Obsolete("Use PluginIcons instead")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Umbraco.Cms.Core.Models;

namespace Umbraco.Cms.Core.Deploy;

/// <summary>
/// Extension methods adding backwards-compatability between <see cref="IDataTypeConfigurationConnector" /> and <see cref="IDataTypeConfigurationConnector2" />.
/// </summary>
/// <remarks>
/// These extension methods will be removed in Umbraco 13.
/// </remarks>
public static class DataTypeConfigurationConnectorExtensions
{
/// <summary>
/// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies.
/// </summary>
/// <param name="connector">The connector.</param>
/// <param name="dataType">The data type.</param>
/// <param name="dependencies">The dependencies.</param>
/// <param name="contextCache">The context cache.</param>
/// <returns>
/// The artifact configuration value.
/// </returns>
/// <remarks>
/// This extension method tries to make use of the <see cref="IContextCache" /> on types also implementing <see cref="IDataTypeConfigurationConnector2" />.
/// </remarks>
public static string? ToArtifact(this IDataTypeConfigurationConnector connector, IDataType dataType, ICollection<ArtifactDependency> dependencies, IContextCache contextCache)
=> connector is IDataTypeConfigurationConnector2 connector2
? connector2.ToArtifact(dataType, dependencies, contextCache)
: connector.ToArtifact(dataType, dependencies);

/// <summary>
/// Gets the data type configuration corresponding to an artifact configuration value.
/// </summary>
/// <param name="connector">The connector.</param>
/// <param name="dataType">The data type.</param>
/// <param name="configuration">The artifact configuration value.</param>
/// <param name="contextCache">The context cache.</param>
/// <returns>
/// The data type configuration.
/// </returns>
/// <remarks>
/// This extension method tries to make use of the <see cref="IContextCache" /> on types also implementing <see cref="IDataTypeConfigurationConnector2" />.
/// </remarks>
public static object? FromArtifact(this IDataTypeConfigurationConnector connector, IDataType dataType, string? configuration, IContextCache contextCache)
=> connector is IDataTypeConfigurationConnector2 connector2
? connector2.FromArtifact(dataType, configuration, contextCache)
: connector.FromArtifact(dataType, configuration);
}
32 changes: 4 additions & 28 deletions src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,41 +27,17 @@ public interface IDataTypeConfigurationConnector
/// <returns>
/// The artifact configuration value.
/// </returns>
[Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")]
string? ToArtifact(IDataType dataType, ICollection<ArtifactDependency> dependencies)
=> ToArtifact(dataType, dependencies, PassThroughCache.Instance);

/// <summary>
/// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies.
/// </summary>
/// <param name="dataType">The data type.</param>
/// <param name="dependencies">The dependencies.</param>
/// <param name="contextCache">The context cache.</param>
/// <returns>
/// The artifact configuration value.
/// </returns>
string? ToArtifact(IDataType dataType, ICollection<ArtifactDependency> dependencies, IContextCache contextCache);

/// <summary>
/// Gets the data type configuration corresponding to an artifact configuration value.
/// </summary>
/// <param name="dataType">The data type.</param>
/// <param name="configuration">The artifact configuration value.</param>
/// <returns>
/// The data type configuration.
/// </returns>
[Obsolete("Use the overload accepting IContextCache instead. This overload will be removed in a future version.")]
object? FromArtifact(IDataType dataType, string? configuration)
=> FromArtifact(dataType, configuration, PassThroughCache.Instance);
[Obsolete($"Implement {nameof(IDataTypeConfigurationConnector2)} and use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")]
string? ToArtifact(IDataType dataType, ICollection<ArtifactDependency> dependencies);

/// <summary>
/// Gets the data type configuration corresponding to an artifact configuration value.
/// </summary>
/// <param name="dataType">The data type.</param>
/// <param name="configuration">The artifact configuration value.</param>
/// <param name="contextCache">The context cache.</param>
/// <returns>
/// The data type configuration.
/// </returns>
object? FromArtifact(IDataType dataType, string? configuration, IContextCache contextCache);
[Obsolete($"Implement {nameof(IDataTypeConfigurationConnector2)} and use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")]
object? FromArtifact(IDataType dataType, string? configuration);
}
Loading