Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: umbraco/Umbraco-CMS
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: contrib
Choose a base ref
...
head repository: umbraco/Umbraco-CMS
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v14/dev
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 10 commits
  • 18 files changed
  • 4 contributors

Commits on Jan 7, 2025

  1. Bumped version to 14.3.2.

    AndyButland committed Jan 7, 2025
    Copy the full SHA
    0166727 View commit details

Commits on Jan 20, 2025

  1. Merge commit from fork

    AndyButland authored Jan 20, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    86f3033 View commit details
  2. update backoffice submodule to 14.3.2

    bergmania authored and iOvergaard committed Jan 20, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    iOvergaard Jacob Overgaard
    Copy the full SHA
    c65204a View commit details
  3. update backoffice submodule to 14.3.2

    iOvergaard committed Jan 20, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    iOvergaard Jacob Overgaard
    Copy the full SHA
    e8d6cde View commit details
  4. Merge commit from fork

    AndyButland authored Jan 20, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    a0a4af6 View commit details
  5. Merge commit from fork

    * Add TimedScope
    
    * Use TimedScope in login endpoint
    
    * Use seperate default duration and only calculate average of actual successful responses
    
    * Only return detailed error responses if credentials are valid
    
    * Cancel timed scope when credentials are valid
    
    * Add UserDefaultFailedLoginDuration and UserMinimumFailedLoginDuration settings
    ronaldbarendse authored and Zeegaan committed Jan 20, 2025
    Copy the full SHA
    65bb280 View commit details

Commits on Jan 21, 2025

  1. update backoffice submodule to 14.3.2

    iOvergaard committed Jan 21, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    iOvergaard Jacob Overgaard
    Copy the full SHA
    abc312c View commit details

Commits on Mar 9, 2025

  1. bumped imagesharp to prevent CVE-2025-27598 (#18602)

    # Conflicts:
    #	Directory.Packages.props
    AndyButland committed Mar 9, 2025
    Copy the full SHA
    14ed334 View commit details

Commits on Mar 11, 2025

  1. Merge commit from fork

    * Bumped version to 15.2.1.
    
    # Conflicts:
    #	version.json
    
    * Tighten management API endpoint access rules.
    AndyButland authored Mar 11, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    d9fb6df View commit details
  2. Merge branch 'release/14.3.3' into v14/dev

    # Conflicts:
    #	Directory.Packages.props
    #	version.json
    AndyButland committed Mar 11, 2025
    Copy the full SHA
    8a9db11 View commit details
Showing with 283 additions and 45 deletions.
  1. +1 −1 Directory.Packages.props
  2. +3 −0 src/Umbraco.Cms.Api.Management/Controllers/DataType/CopyDataTypeController.cs
  3. +4 −1 src/Umbraco.Cms.Api.Management/Controllers/DataType/CreateDataTypeController.cs
  4. +4 −1 src/Umbraco.Cms.Api.Management/Controllers/DataType/DeleteDataTypeController.cs
  5. +3 −0 src/Umbraco.Cms.Api.Management/Controllers/DataType/MoveDataTypeController.cs
  6. +4 −1 src/Umbraco.Cms.Api.Management/Controllers/DataType/UpdateDataTypeController.cs
  7. +3 −0 src/Umbraco.Cms.Api.Management/Controllers/DocumentType/ExportDocumentTypeController.cs
  8. +3 −1 src/Umbraco.Cms.Api.Management/Controllers/DocumentType/ImportExistingDocumentTypeController.cs
  9. +3 −1 src/Umbraco.Cms.Api.Management/Controllers/DocumentType/ImportNewDocumentTypeController.cs
  10. +3 −0 src/Umbraco.Cms.Api.Management/Controllers/MediaType/ExportMediaTypeController.cs
  11. +3 −1 src/Umbraco.Cms.Api.Management/Controllers/MediaType/ImportExistingMediaTypeController.cs
  12. +3 −2 src/Umbraco.Cms.Api.Management/Controllers/MediaType/ImportNewMediaTypeController.cs
  13. +1 −2 src/Umbraco.Cms.Api.Management/Controllers/MediaType/MediaTypeControllerBase.cs
  14. +54 −32 src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs
  15. +1 −1 src/Umbraco.Cms.Imaging.ImageSharp2/Umbraco.Cms.Imaging.ImageSharp2.csproj
  16. +27 −0 src/Umbraco.Core/Configuration/Models/SecuritySettings.cs
  17. +162 −0 src/Umbraco.Core/TimedScope.cs
  18. +1 −1 src/Umbraco.Web.UI.Client
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -73,7 +73,7 @@
<PackageVersion Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageVersion Include="Serilog.Sinks.Map" Version="1.0.2" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.6" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.7" />
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.1.3" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.9.0" />
</ItemGroup>
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.DataType;
@@ -7,10 +8,12 @@
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.DataType;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDataTypes)]
public class CopyDataTypeController : DataTypeControllerBase
{
private readonly IDataTypeService _dataTypeService;
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
@@ -8,10 +9,12 @@
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.DataType;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDataTypes)]
public class CreateDataTypeController : DataTypeControllerBase
{
private readonly IDataTypeService _dataTypeService;
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.DataType;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDataTypes)]
public class DeleteDataTypeController : DataTypeControllerBase
{
private readonly IDataTypeService _dataTypeService;
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.DataType;
@@ -7,10 +8,12 @@
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.DataType;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDataTypes)]
public class MoveDataTypeController : DataTypeControllerBase
{
private readonly IDataTypeService _dataTypeService;
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Asp.Versioning;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
@@ -8,10 +9,12 @@
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.DataType;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDataTypes)]
public class UpdateDataTypeController : DataTypeControllerBase
{
private readonly IDataTypeService _dataTypeService;
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.DocumentType;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
public class ExportDocumentTypeController : DocumentTypeControllerBase
{
private readonly IContentTypeService _contentTypeService;
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services.ImportExport;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.DocumentType;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
public class ImportExistingDocumentTypeController : DocumentTypeControllerBase
{
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services.ImportExport;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.DocumentType;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
public class ImportNewDocumentTypeController : DocumentTypeControllerBase
{
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.MediaType;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)]
public class ExportMediaTypeController : MediaTypeControllerBase
{
private readonly IMediaTypeService _mediaTypeService;
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.MediaType;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services.ImportExport;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.MediaType;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)]
public class ImportExistingMediaTypeController : MediaTypeControllerBase
{
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Controllers.DocumentType;
using Umbraco.Cms.Api.Management.ViewModels.MediaType;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services.ImportExport;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.MediaType;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)]
public class ImportNewMediaTypeController : MediaTypeControllerBase
{
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Controllers.DocumentType;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Api.Management.ViewModels.MediaType;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;
Original file line number Diff line number Diff line change
@@ -34,6 +34,8 @@ namespace Umbraco.Cms.Api.Management.Controllers.Security;
[ApiExplorerSettings(IgnoreApi = true)]
public class BackOfficeController : SecurityControllerBase
{
private static long? _loginDurationAverage;

private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IBackOfficeSignInManager _backOfficeSignInManager;
private readonly IBackOfficeUserManager _backOfficeUserManager;
@@ -72,45 +74,65 @@ public BackOfficeController(
[Authorize(Policy = AuthorizationPolicies.DenyLocalLoginIfConfigured)]
public async Task<IActionResult> Login(CancellationToken cancellationToken, LoginRequestModel model)
{
IdentitySignInResult result = await _backOfficeSignInManager.PasswordSignInAsync(
model.Username, model.Password, true, true);
// Start a timed scope to ensure failed responses return is a consistent time
var loginDuration = Math.Max(_loginDurationAverage ?? _securitySettings.Value.UserDefaultFailedLoginDurationInMilliseconds, _securitySettings.Value.UserMinimumFailedLoginDurationInMilliseconds);
await using var timedScope = new TimedScope(loginDuration, cancellationToken);

if (result.IsNotAllowed)
IdentitySignInResult result = await _backOfficeSignInManager.PasswordSignInAsync(model.Username, model.Password, true, true);
if (result.Succeeded is false)
{
return StatusCode(StatusCodes.Status403Forbidden, new ProblemDetailsBuilder()
.WithTitle("User is not allowed")
.WithDetail("The operation is not allowed on the user")
.Build());
}
// TODO: The result should include the user and whether the credentials were valid to avoid these additional checks
BackOfficeIdentityUser? user = await _backOfficeUserManager.FindByNameAsync(model.Username.Trim()); // Align with UmbracoSignInManager and trim username!
if (user is not null &&
await _backOfficeUserManager.CheckPasswordAsync(user, model.Password))
{
// The credentials were correct, so cancel timed scope and provide a more detailed failure response
await timedScope.CancelAsync();

if (result.IsNotAllowed)
{
return StatusCode(StatusCodes.Status403Forbidden, new ProblemDetailsBuilder()
.WithTitle("User is not allowed")
.WithDetail("The operation is not allowed on the user")
.Build());
}

if (result.IsLockedOut)
{
return StatusCode(StatusCodes.Status403Forbidden, new ProblemDetailsBuilder()
.WithTitle("User is locked")
.WithDetail("The user is locked, and need to be unlocked before more login attempts can be executed.")
.Build());
}

if (result.RequiresTwoFactor)
{
string? twofactorView = _backOfficeTwoFactorOptions.GetTwoFactorView(model.Username);
IEnumerable<string> enabledProviders = (await _userTwoFactorLoginService.GetProviderNamesAsync(user.Key)).Result.Where(x => x.IsEnabledOnUser).Select(x => x.ProviderName);

return StatusCode(StatusCodes.Status402PaymentRequired, new RequiresTwoFactorResponseModel()
{
TwoFactorLoginView = twofactorView,
EnabledTwoFactorProviderNames = enabledProviders
});
}
}

if (result.IsLockedOut)
{
return StatusCode(StatusCodes.Status403Forbidden, new ProblemDetailsBuilder()
.WithTitle("User is locked")
.WithDetail("The user is locked, and need to be unlocked before more login attempts can be executed.")
return StatusCode(StatusCodes.Status401Unauthorized, new ProblemDetailsBuilder()
.WithTitle("Invalid credentials")
.WithDetail("The provided credentials are invalid. User has not been signed in.")
.Build());
}

if(result.RequiresTwoFactor)
{
string? twofactorView = _backOfficeTwoFactorOptions.GetTwoFactorView(model.Username);
BackOfficeIdentityUser? attemptingUser = await _backOfficeUserManager.FindByNameAsync(model.Username);
IEnumerable<string> enabledProviders = (await _userTwoFactorLoginService.GetProviderNamesAsync(attemptingUser!.Key)).Result.Where(x=>x.IsEnabledOnUser).Select(x=>x.ProviderName);
return StatusCode(StatusCodes.Status402PaymentRequired, new RequiresTwoFactorResponseModel()
{
TwoFactorLoginView = twofactorView,
EnabledTwoFactorProviderNames = enabledProviders
});
}
// Set initial or update average (successful) login duration
_loginDurationAverage = _loginDurationAverage is long average
? (average + (long)timedScope.Elapsed.TotalMilliseconds) / 2
: (long)timedScope.Elapsed.TotalMilliseconds;

if (result.Succeeded)
{
return Ok();
}
return StatusCode(StatusCodes.Status401Unauthorized, new ProblemDetailsBuilder()
.WithTitle("Invalid credentials")
.WithDetail("The provided credentials are invalid. User has not been signed in.")
.Build());
// Cancel the timed scope (we don't want to unnecessarily wait on a successful response)
await timedScope.CancelAsync();

return Ok();
}

[AllowAnonymous]
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
<Description>Adds imaging support using ImageSharp/ImageSharp.Web version 2 to Umbraco CMS.</Description>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" VersionOverride="[2.1.9, 3)" />
<PackageReference Include="SixLabors.ImageSharp" VersionOverride="[2.1.10, 3)" />
<PackageReference Include="SixLabors.ImageSharp.Web" VersionOverride="[2.0.2, 3)" />
</ItemGroup>

Loading