diff --git a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDatabaseProviderMetadata.cs b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDatabaseProviderMetadata.cs
index edb44700d8..89612b3603 100644
--- a/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDatabaseProviderMetadata.cs
+++ b/src/Umbraco.Cms.Persistence.SqlServer/Services/SqlServerDatabaseProviderMetadata.cs
@@ -50,7 +50,7 @@ public class SqlServerDatabaseProviderMetadata : IDatabaseProviderMetadata
public bool RequiresConnectionTest => true;
///
- public bool ForceCreateDatabase => false;
+ public bool ForceCreateDatabase => true;
///
public bool CanRecognizeConnectionString(string? connectionString)
diff --git a/src/Umbraco.Core/Actions/ActionToPublish.cs b/src/Umbraco.Core/Actions/ActionToPublish.cs
index e7af16bc99..25719e30fc 100644
--- a/src/Umbraco.Core/Actions/ActionToPublish.cs
+++ b/src/Umbraco.Core/Actions/ActionToPublish.cs
@@ -6,6 +6,7 @@ namespace Umbraco.Cms.Core.Actions;
///
/// This action is invoked when children to a document is being sent to published (by an editor without publishrights).
///
+[Obsolete("Scheduled for removal in v13")]
public class ActionToPublish : IAction
{
///
diff --git a/src/Umbraco.Core/Events/UserNotificationsHandler.cs b/src/Umbraco.Core/Events/UserNotificationsHandler.cs
index 042355630f..4b581788e8 100644
--- a/src/Umbraco.Core/Events/UserNotificationsHandler.cs
+++ b/src/Umbraco.Core/Events/UserNotificationsHandler.cs
@@ -107,6 +107,7 @@ public sealed class UserNotificationsHandler :
_notifier.Notify(_actions.GetAction(), updatedEntities.ToArray());
}
+ [Obsolete("Scheduled for removal in v13")]
public void Handle(ContentSentToPublishNotification notification) =>
_notifier.Notify(_actions.GetAction(), notification.Entity);
diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs
index 1dd2fef124..1164b02753 100644
--- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedValueFallback.cs
@@ -7,7 +7,15 @@ namespace Umbraco.Cms.Core.Models.PublishedContent;
/// This is for tests etc - does not implement fallback at all.
///
public class NoopPublishedValueFallback : IPublishedValueFallback
+
{
+ ///
+ public IVariationContextAccessor VariationContextAccessor
+ {
+ get => new ThreadCultureVariationContextAccessor();
+ set { }
+ }
+
///
public bool TryGetValue(IPublishedProperty property, string? culture, string? segment, Fallback fallback, object? defaultValue, out object? value)
{
diff --git a/src/Umbraco.Core/Services/MetricsConsentService.cs b/src/Umbraco.Core/Services/MetricsConsentService.cs
index a3140c4f61..be30458af6 100644
--- a/src/Umbraco.Core/Services/MetricsConsentService.cs
+++ b/src/Umbraco.Core/Services/MetricsConsentService.cs
@@ -60,7 +60,7 @@ public class MetricsConsentService : IMetricsConsentService
if (analyticsLevelString is null ||
Enum.TryParse(analyticsLevelString, out TelemetryLevel analyticsLevel) is false)
{
- return TelemetryLevel.Basic;
+ return TelemetryLevel.Detailed;
}
return analyticsLevel;
diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedPropertyIndexValueFactoryBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedPropertyIndexValueFactoryBase.cs
index fa3ae836c1..4eb7051745 100644
--- a/src/Umbraco.Infrastructure/PropertyEditors/NestedPropertyIndexValueFactoryBase.cs
+++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedPropertyIndexValueFactoryBase.cs
@@ -38,7 +38,7 @@ internal abstract class NestedPropertyIndexValueFactoryBase
var propertyTypeDictionary =
contentType
- .PropertyGroups
+ .CompositionPropertyGroups
.SelectMany(x => x.PropertyTypes!)
.Select(propertyType =>
{
diff --git a/src/Umbraco.New.Cms.Core/Models/Installer/InstallData.cs b/src/Umbraco.New.Cms.Core/Models/Installer/InstallData.cs
index 2283cf2482..77d0c07477 100644
--- a/src/Umbraco.New.Cms.Core/Models/Installer/InstallData.cs
+++ b/src/Umbraco.New.Cms.Core/Models/Installer/InstallData.cs
@@ -8,5 +8,5 @@ public class InstallData
public DatabaseInstallData Database { get; set; } = null!;
- public TelemetryLevel TelemetryLevel { get; set; } = TelemetryLevel.Basic;
+ public TelemetryLevel TelemetryLevel { get; set; } = TelemetryLevel.Detailed;
}
diff --git a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs
index fb1666e522..73cd71918d 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs
@@ -278,7 +278,7 @@ public class CurrentUserController : UmbracoAuthorizedJsonController
// all current users have access to reset/manually change their password
Attempt passwordChangeResult =
- await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _backOfficeUserManager);
+ await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _backOfficeUserManager, currentUser);
if (passwordChangeResult.Success)
{
diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
index f06ae05f86..c190891217 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs
@@ -623,7 +623,7 @@ public class MemberController : ContentControllerBase
// Change and persist the password
Attempt passwordChangeResult =
- await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _memberManager);
+ await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _memberManager, _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser);
if (!passwordChangeResult.Success)
{
diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
index 02eb9cda8e..3e14154b48 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs
@@ -759,7 +759,7 @@ public class UsersController : BackOfficeNotificationsController
}
Attempt passwordChangeResult =
- await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _userManager);
+ await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _userManager, currentUser);
if (passwordChangeResult.Success)
{
diff --git a/src/Umbraco.Web.BackOffice/Security/IPasswordChanger.cs b/src/Umbraco.Web.BackOffice/Security/IPasswordChanger.cs
index 66c69d4d70..3bc5f35abf 100644
--- a/src/Umbraco.Web.BackOffice/Security/IPasswordChanger.cs
+++ b/src/Umbraco.Web.BackOffice/Security/IPasswordChanger.cs
@@ -1,12 +1,19 @@
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
namespace Umbraco.Cms.Web.Common.Security;
public interface IPasswordChanger where TUser : UmbracoIdentityUser
{
+ [Obsolete("Please use method that also takes a nullable IUser, scheduled for removal in v13")]
public Task> ChangePasswordWithIdentityAsync(
ChangingPasswordModel passwordModel,
IUmbracoUserManager userMgr);
+
+ public Task> ChangePasswordWithIdentityAsync(
+ ChangingPasswordModel passwordModel,
+ IUmbracoUserManager userMgr,
+ IUser? currentUser) => ChangePasswordWithIdentityAsync(passwordModel, userMgr);
}
diff --git a/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs b/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs
index 25f0548386..8b74f6d2c3 100644
--- a/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs
+++ b/src/Umbraco.Web.BackOffice/Security/PasswordChanger.cs
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Extensions;
@@ -22,16 +23,20 @@ internal class PasswordChanger : IPasswordChanger where TUser : Um
/// Logger for this class
public PasswordChanger(ILogger> logger) => _logger = logger;
+ public Task> ChangePasswordWithIdentityAsync(ChangingPasswordModel passwordModel, IUmbracoUserManager userMgr) => ChangePasswordWithIdentityAsync(passwordModel, userMgr, null);
+
///
/// Changes the password for a user based on the many different rules and config options
///
- /// The changing password model
- /// The identity manager to use to update the password
+ /// The changing password model.
+ /// The identity manager to use to update the password.
+ /// The user performing the operation.
/// Create an adapter to pass through everything - adapting the member into a user for this functionality
/// The outcome of the password changed model
public async Task> ChangePasswordWithIdentityAsync(
ChangingPasswordModel changingPasswordModel,
- IUmbracoUserManager userMgr)
+ IUmbracoUserManager userMgr,
+ IUser? currentUser)
{
if (changingPasswordModel == null)
{
@@ -65,6 +70,14 @@ internal class PasswordChanger : IPasswordChanger where TUser : Um
// Are we just changing another user/member's password?
if (changingPasswordModel.OldPassword.IsNullOrWhiteSpace())
{
+ if (changingPasswordModel.Id == currentUser?.Id)
+ {
+ return Attempt.Fail(new PasswordChangedModel
+ {
+ ChangeError = new ValidationResult("Cannot change the password of current user without the old password", new[] { "value" }),
+ });
+ }
+
// ok, we should be able to reset it
var resetToken = await userMgr.GeneratePasswordResetTokenAsync(identityUser);
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs
index e8f381699a..4b192d556f 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/TelemetryServiceTests.cs
@@ -83,7 +83,7 @@ public class TelemetryServiceTests
};
var manifestParser = CreateManifestParser(manifests);
var metricsConsentService = new Mock();
- metricsConsentService.Setup(x => x.GetConsentLevel()).Returns(TelemetryLevel.Basic);
+ metricsConsentService.Setup(x => x.GetConsentLevel()).Returns(TelemetryLevel.Detailed);
var sut = new TelemetryService(
manifestParser,
version,
@@ -118,7 +118,7 @@ public class TelemetryServiceTests
};
var manifestParser = CreateManifestParser(manifests);
var metricsConsentService = new Mock();
- metricsConsentService.Setup(x => x.GetConsentLevel()).Returns(TelemetryLevel.Basic);
+ metricsConsentService.Setup(x => x.GetConsentLevel()).Returns(TelemetryLevel.Detailed);
var sut = new TelemetryService(
manifestParser,
version,
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs
index b9c6a63812..84aa1173ef 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs
@@ -358,7 +358,8 @@ public class MemberControllerUnitTests
Mock.Get(passwordChanger)
.Setup(x => x.ChangePasswordWithIdentityAsync(
It.IsAny(),
- umbracoMembersUserManager))
+ umbracoMembersUserManager,
+ null))
.ReturnsAsync(() => attempt);
}
else
@@ -367,7 +368,8 @@ public class MemberControllerUnitTests
Mock.Get(passwordChanger)
.Setup(x => x.ChangePasswordWithIdentityAsync(
It.IsAny(),
- umbracoMembersUserManager))
+ umbracoMembersUserManager,
+ null))
.ReturnsAsync(() => attempt);
}
}