Added validation and moved Unattended Settings into their own model. + Added DataAnnonation validation on all our settings
This commit is contained in:
@@ -84,33 +84,6 @@ namespace Umbraco.Cms.Core.Configuration.Models
|
||||
/// </summary>
|
||||
public bool InstallMissingDatabase { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether unattended installs are enabled.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>By default, when a database connection string is configured and it is possible to connect to
|
||||
/// the database, but the database is empty, the runtime enters the <c>Install</c> level.
|
||||
/// If this option is set to <c>true</c> an unattended install will be performed and the runtime enters
|
||||
/// the <c>Run</c> level.</para>
|
||||
/// </remarks>
|
||||
public bool InstallUnattended { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value to use for creating a user with a name for Unattended Installs
|
||||
/// </summary>
|
||||
public string UnattendedUserName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value to use for creating a user with an email for Unattended Installs
|
||||
/// </summary>
|
||||
public string UnattendedUserEmail { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value to use for creating a user with a password for Unattended Installs
|
||||
/// </summary>
|
||||
public string UnattendedUserPassword { get; set; } = string.Empty;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to disable the election for a single server.
|
||||
/// </summary>
|
||||
|
||||
38
src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs
Normal file
38
src/Umbraco.Core/Configuration/Models/UnattendedSettings.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Umbraco.Cms.Core.Configuration.Models
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Typed configuration options for unattended settings.
|
||||
/// </summary>
|
||||
public class UnattendedSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether unattended installs are enabled.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>By default, when a database connection string is configured and it is possible to connect to
|
||||
/// the database, but the database is empty, the runtime enters the <c>Install</c> level.
|
||||
/// If this option is set to <c>true</c> an unattended install will be performed and the runtime enters
|
||||
/// the <c>Run</c> level.</para>
|
||||
/// </remarks>
|
||||
public bool InstallUnattended { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value to use for creating a user with a name for Unattended Installs
|
||||
/// </summary>
|
||||
public string UnattendedUserName { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value to use for creating a user with an email for Unattended Installs
|
||||
/// </summary>
|
||||
[EmailAddress]
|
||||
public string UnattendedUserEmail { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value to use for creating a user with a password for Unattended Installs
|
||||
/// </summary>
|
||||
public string UnattendedUserPassword { get; set; } = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Umbraco.Cms.Core.Configuration.Models.Validation
|
||||
{
|
||||
/// <summary>
|
||||
/// Validator for configuration representated as <see cref="UnattendedSettings"/>.
|
||||
/// </summary>
|
||||
public class UnattendedSettingsValidator
|
||||
: IValidateOptions<UnattendedSettings>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ValidateOptionsResult Validate(string name, UnattendedSettings options)
|
||||
{
|
||||
if (options.InstallUnattended)
|
||||
{
|
||||
int setValues = 0;
|
||||
if (!string.IsNullOrEmpty(options.UnattendedUserName))
|
||||
{
|
||||
setValues++;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(options.UnattendedUserEmail))
|
||||
{
|
||||
setValues++;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(options.UnattendedUserPassword))
|
||||
{
|
||||
setValues++;
|
||||
}
|
||||
|
||||
if (0 < setValues && setValues < 3)
|
||||
{
|
||||
return ValidateOptionsResult.Fail($"Configuration entry {Constants.Configuration.ConfigUnattended} contains invalid values.\nIf any of the {nameof(options.UnattendedUserName)}, {nameof(options.UnattendedUserEmail)}, {nameof(options.UnattendedUserPassword)} are set, all of them are required.");
|
||||
}
|
||||
}
|
||||
|
||||
return ValidateOptionsResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
public const string ConfigCoreDebug = ConfigCorePrefix + "Debug";
|
||||
public const string ConfigExceptionFilter = ConfigPrefix + "ExceptionFilter";
|
||||
public const string ConfigGlobal = ConfigPrefix + "Global";
|
||||
public const string ConfigUnattended = ConfigPrefix + "Unattended";
|
||||
public const string ConfigHealthChecks = ConfigPrefix + "HealthChecks";
|
||||
public const string ConfigHosting = ConfigPrefix + "Hosting";
|
||||
public const string ConfigImaging = ConfigPrefix + "Imaging";
|
||||
|
||||
@@ -10,6 +10,14 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
/// </summary>
|
||||
public static partial class UmbracoBuilderExtensions
|
||||
{
|
||||
|
||||
private static OptionsBuilder<TOptions> AddOptions<TOptions>(IUmbracoBuilder builder, string key)
|
||||
where TOptions : class
|
||||
{
|
||||
return builder.Services.AddOptions<TOptions>()
|
||||
.Bind(builder.Config.GetSection(key))
|
||||
.ValidateDataAnnotations();
|
||||
}
|
||||
/// <summary>
|
||||
/// Add Umbraco configuration services and options
|
||||
/// </summary>
|
||||
@@ -20,31 +28,34 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
builder.Services.AddSingleton<IValidateOptions<GlobalSettings>, GlobalSettingsValidator>();
|
||||
builder.Services.AddSingleton<IValidateOptions<HealthChecksSettings>, HealthChecksSettingsValidator>();
|
||||
builder.Services.AddSingleton<IValidateOptions<RequestHandlerSettings>, RequestHandlerSettingsValidator>();
|
||||
builder.Services.AddSingleton<IValidateOptions<UnattendedSettings>, UnattendedSettingsValidator>();
|
||||
|
||||
// Register configuration sections.
|
||||
builder.Services.Configure<ActiveDirectorySettings>(builder.Config.GetSection(Constants.Configuration.ConfigActiveDirectory));
|
||||
builder.Services.Configure<ConnectionStrings>(builder.Config.GetSection(Constants.Configuration.ConfigModelsBuilder), o => o.BindNonPublicProperties = true);
|
||||
builder.Services.Configure<ConnectionStrings>(builder.Config.GetSection("ConnectionStrings"), o => o.BindNonPublicProperties = true);
|
||||
builder.Services.Configure<ContentSettings>(builder.Config.GetSection(Constants.Configuration.ConfigContent));
|
||||
builder.Services.Configure<CoreDebugSettings>(builder.Config.GetSection(Constants.Configuration.ConfigCoreDebug));
|
||||
builder.Services.Configure<ExceptionFilterSettings>(builder.Config.GetSection(Constants.Configuration.ConfigExceptionFilter));
|
||||
builder.Services.Configure<GlobalSettings>(builder.Config.GetSection(Constants.Configuration.ConfigGlobal));
|
||||
builder.Services.Configure<HealthChecksSettings>(builder.Config.GetSection(Constants.Configuration.ConfigHealthChecks));
|
||||
builder.Services.Configure<HostingSettings>(builder.Config.GetSection(Constants.Configuration.ConfigHosting));
|
||||
builder.Services.Configure<ImagingSettings>(builder.Config.GetSection(Constants.Configuration.ConfigImaging));
|
||||
builder.Services.Configure<IndexCreatorSettings>(builder.Config.GetSection(Constants.Configuration.ConfigExamine));
|
||||
builder.Services.Configure<KeepAliveSettings>(builder.Config.GetSection(Constants.Configuration.ConfigKeepAlive));
|
||||
builder.Services.Configure<LoggingSettings>(builder.Config.GetSection(Constants.Configuration.ConfigLogging));
|
||||
builder.Services.Configure<MemberPasswordConfigurationSettings>(builder.Config.GetSection(Constants.Configuration.ConfigMemberPassword));
|
||||
builder.Services.Configure<ModelsBuilderSettings>(builder.Config.GetSection(Constants.Configuration.ConfigModelsBuilder), o => o.BindNonPublicProperties = true);
|
||||
builder.Services.Configure<NuCacheSettings>(builder.Config.GetSection(Constants.Configuration.ConfigNuCache));
|
||||
builder.Services.Configure<RequestHandlerSettings>(builder.Config.GetSection(Constants.Configuration.ConfigRequestHandler));
|
||||
builder.Services.Configure<RuntimeSettings>(builder.Config.GetSection(Constants.Configuration.ConfigRuntime));
|
||||
builder.Services.Configure<SecuritySettings>(builder.Config.GetSection(Constants.Configuration.ConfigSecurity));
|
||||
builder.Services.Configure<TourSettings>(builder.Config.GetSection(Constants.Configuration.ConfigTours));
|
||||
builder.Services.Configure<TypeFinderSettings>(builder.Config.GetSection(Constants.Configuration.ConfigTypeFinder));
|
||||
builder.Services.Configure<UserPasswordConfigurationSettings>(builder.Config.GetSection(Constants.Configuration.ConfigUserPassword));
|
||||
builder.Services.Configure<WebRoutingSettings>(builder.Config.GetSection(Constants.Configuration.ConfigWebRouting));
|
||||
builder.Services.Configure<UmbracoPluginSettings>(builder.Config.GetSection(Constants.Configuration.ConfigPlugins));
|
||||
|
||||
AddOptions<ActiveDirectorySettings>(builder, Constants.Configuration.ConfigActiveDirectory);
|
||||
AddOptions<ContentSettings>(builder, Constants.Configuration.ConfigContent);
|
||||
AddOptions<CoreDebugSettings>(builder, Constants.Configuration.ConfigCoreDebug);
|
||||
AddOptions<ExceptionFilterSettings>(builder, Constants.Configuration.ConfigExceptionFilter);
|
||||
AddOptions<GlobalSettings>(builder, Constants.Configuration.ConfigGlobal);
|
||||
AddOptions<HealthChecksSettings>(builder, Constants.Configuration.ConfigHealthChecks);
|
||||
AddOptions<HostingSettings>(builder, Constants.Configuration.ConfigHosting);
|
||||
AddOptions<ImagingSettings>(builder, Constants.Configuration.ConfigImaging);
|
||||
AddOptions<IndexCreatorSettings>(builder, Constants.Configuration.ConfigExamine);
|
||||
AddOptions<KeepAliveSettings>(builder, Constants.Configuration.ConfigKeepAlive);
|
||||
AddOptions<LoggingSettings>(builder, Constants.Configuration.ConfigLogging);
|
||||
AddOptions<MemberPasswordConfigurationSettings>(builder, Constants.Configuration.ConfigMemberPassword);
|
||||
AddOptions<NuCacheSettings>(builder, Constants.Configuration.ConfigNuCache);
|
||||
AddOptions<RequestHandlerSettings>(builder, Constants.Configuration.ConfigRequestHandler);
|
||||
AddOptions<RuntimeSettings>(builder, Constants.Configuration.ConfigRuntime);
|
||||
AddOptions<SecuritySettings>(builder, Constants.Configuration.ConfigSecurity);
|
||||
AddOptions<TourSettings>(builder, Constants.Configuration.ConfigTours);
|
||||
AddOptions<TypeFinderSettings>(builder, Constants.Configuration.ConfigTypeFinder);
|
||||
AddOptions<UserPasswordConfigurationSettings>(builder, Constants.Configuration.ConfigUserPassword);
|
||||
AddOptions<WebRoutingSettings>(builder, Constants.Configuration.ConfigWebRouting);
|
||||
AddOptions<UmbracoPluginSettings>(builder, Constants.Configuration.ConfigPlugins);
|
||||
AddOptions<UnattendedSettings>(builder, Constants.Configuration.ConfigUnattended);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="5.0.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" />
|
||||
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace Umbraco.Cms.Core
|
||||
/// </summary>
|
||||
public class RuntimeState : IRuntimeState
|
||||
{
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IOptions<GlobalSettings> _globalSettings;
|
||||
private readonly IOptions<UnattendedSettings> _unattendedSettings;
|
||||
private readonly IUmbracoVersion _umbracoVersion;
|
||||
private readonly IUmbracoDatabaseFactory _databaseFactory;
|
||||
private readonly ILogger<RuntimeState> _logger;
|
||||
@@ -41,13 +42,15 @@ namespace Umbraco.Cms.Core
|
||||
/// </summary>
|
||||
public RuntimeState(
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
IOptions<UnattendedSettings> unattendedSettings,
|
||||
IUmbracoVersion umbracoVersion,
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
ILogger<RuntimeState> logger,
|
||||
DatabaseSchemaCreatorFactory databaseSchemaCreatorFactory,
|
||||
IEventAggregator eventAggregator)
|
||||
{
|
||||
_globalSettings = globalSettings.Value;
|
||||
_globalSettings = globalSettings;
|
||||
_unattendedSettings = unattendedSettings;
|
||||
_umbracoVersion = umbracoVersion;
|
||||
_databaseFactory = databaseFactory;
|
||||
_logger = logger;
|
||||
@@ -102,7 +105,7 @@ namespace Umbraco.Cms.Core
|
||||
// cannot connect to configured database, this is bad, fail
|
||||
_logger.LogDebug("Could not connect to database.");
|
||||
|
||||
if (_globalSettings.InstallMissingDatabase)
|
||||
if (_globalSettings.Value.InstallMissingDatabase)
|
||||
{
|
||||
// ok to install on a configured but missing database
|
||||
Level = RuntimeLevel.Install;
|
||||
@@ -201,13 +204,13 @@ namespace Umbraco.Cms.Core
|
||||
public void DoUnattendedInstall()
|
||||
{
|
||||
// unattended install is not enabled
|
||||
if (_globalSettings.InstallUnattended == false) return;
|
||||
if (_unattendedSettings.Value.InstallUnattended == false) return;
|
||||
|
||||
// no connection string set
|
||||
if (_databaseFactory.Configured == false) return;
|
||||
|
||||
var connect = false;
|
||||
var tries = _globalSettings.InstallMissingDatabase ? 2 : 5;
|
||||
var tries = _globalSettings.Value.InstallMissingDatabase ? 2 : 5;
|
||||
for (var i = 0;;)
|
||||
{
|
||||
connect = _databaseFactory.CanConnect;
|
||||
@@ -288,7 +291,7 @@ namespace Umbraco.Cms.Core
|
||||
// anything other than install wants a database - see if we can connect
|
||||
// (since this is an already existing database, assume localdb is ready)
|
||||
bool canConnect;
|
||||
var tries = _globalSettings.InstallMissingDatabase ? 2 : 5;
|
||||
var tries = _globalSettings.Value.InstallMissingDatabase ? 2 : 5;
|
||||
for (var i = 0; ;)
|
||||
{
|
||||
canConnect = databaseFactory.CanConnect;
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Umbraco.Cms.Tests.Integration.Testing
|
||||
[SetUp]
|
||||
public virtual void Setup()
|
||||
{
|
||||
InMemoryConfiguration[Constants.Configuration.ConfigGlobal + ":" + nameof(GlobalSettings.InstallUnattended)] = "true";
|
||||
InMemoryConfiguration[Constants.Configuration.ConfigUnattended + ":" + nameof(UnattendedSettings.InstallUnattended)] = "true";
|
||||
IHostBuilder hostBuilder = CreateHostBuilder();
|
||||
|
||||
IHost host = hostBuilder.Build();
|
||||
|
||||
@@ -16,83 +16,84 @@ namespace Umbraco.Cms.Web.Common.Install
|
||||
{
|
||||
public class CreateUnattendedUserNotificationHandler : INotificationAsyncHandler<UnattendedInstallNotification>
|
||||
{
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IOptions<UnattendedSettings> _unattendedSettings;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
|
||||
public CreateUnattendedUserNotificationHandler(IOptions<GlobalSettings> globalSettings, IUserService userService, IServiceScopeFactory serviceScopeFactory)
|
||||
public CreateUnattendedUserNotificationHandler(IOptions<UnattendedSettings> unattendedSettings, IUserService userService, IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
_globalSettings = globalSettings.Value;
|
||||
_unattendedSettings = unattendedSettings;
|
||||
_userService = userService;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Listening for when the UnattendedInstallNotification fired after a sucessfulk
|
||||
/// </summary>
|
||||
/// <param name="notification"></param>
|
||||
public async Task HandleAsync(UnattendedInstallNotification notification, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
// Ensure we have the setting enabled (Sanity check)
|
||||
// In theory this should always be true as the event only fired when a sucessfull
|
||||
if (_globalSettings.InstallUnattended == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var unattendedSettings = _unattendedSettings.Value;
|
||||
// Ensure we have the setting enabled (Sanity check)
|
||||
// In theory this should always be true as the event only fired when a sucessfull
|
||||
if (_unattendedSettings.Value.InstallUnattended == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var unattendedName = _globalSettings.UnattendedUserName;
|
||||
var unattendedEmail = _globalSettings.UnattendedUserEmail;
|
||||
var unattendedPassword = _globalSettings.UnattendedUserPassword;
|
||||
var unattendedName = unattendedSettings.UnattendedUserName;
|
||||
var unattendedEmail = unattendedSettings.UnattendedUserEmail;
|
||||
var unattendedPassword = unattendedSettings.UnattendedUserPassword;
|
||||
|
||||
// Missing configuration values (json, env variables etc)
|
||||
if (unattendedName.IsNullOrWhiteSpace()
|
||||
|| unattendedEmail.IsNullOrWhiteSpace()
|
||||
|| unattendedPassword.IsNullOrWhiteSpace())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Missing configuration values (json, env variables etc)
|
||||
if (unattendedName.IsNullOrWhiteSpace()
|
||||
|| unattendedEmail.IsNullOrWhiteSpace()
|
||||
|| unattendedPassword.IsNullOrWhiteSpace())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IUser admin = _userService.GetUserById(Core.Constants.Security.SuperUserId);
|
||||
if (admin == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find the super user!");
|
||||
}
|
||||
IUser admin = _userService.GetUserById(Core.Constants.Security.SuperUserId);
|
||||
if (admin == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find the super user!");
|
||||
}
|
||||
|
||||
// User email/login has already been modified
|
||||
if (admin.Email == unattendedEmail)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// User email/login has already been modified
|
||||
if (admin.Email == unattendedEmail)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update name, email & login & save user
|
||||
admin.Name = unattendedName.Trim();
|
||||
admin.Email = unattendedEmail.Trim();
|
||||
admin.Username = unattendedEmail.Trim();
|
||||
_userService.Save(admin);
|
||||
// Update name, email & login & save user
|
||||
admin.Name = unattendedName.Trim();
|
||||
admin.Email = unattendedEmail.Trim();
|
||||
admin.Username = unattendedEmail.Trim();
|
||||
_userService.Save(admin);
|
||||
|
||||
// Change Password for the default user we ship out of the box
|
||||
// Uses same approach as NewInstall Step
|
||||
// Change Password for the default user we ship out of the box
|
||||
// Uses same approach as NewInstall Step
|
||||
using IServiceScope scope = _serviceScopeFactory.CreateScope();
|
||||
IBackOfficeUserManager backOfficeUserManager = scope.ServiceProvider.GetRequiredService<IBackOfficeUserManager>();
|
||||
BackOfficeIdentityUser membershipUser = await backOfficeUserManager.FindByIdAsync(Core.Constants.Security.SuperUserId.ToString());
|
||||
if (membershipUser == null)
|
||||
{
|
||||
throw new InvalidOperationException($"No user found in membership provider with id of {Core.Constants.Security.SuperUserId}.");
|
||||
}
|
||||
|
||||
using IServiceScope scope = _serviceScopeFactory.CreateScope();
|
||||
IBackOfficeUserManager backOfficeUserManager = scope.ServiceProvider.GetRequiredService<IBackOfficeUserManager>();
|
||||
BackOfficeIdentityUser membershipUser = await backOfficeUserManager.FindByIdAsync(Core.Constants.Security.SuperUserId.ToString());
|
||||
if (membershipUser == null)
|
||||
{
|
||||
throw new InvalidOperationException($"No user found in membership provider with id of {Core.Constants.Security.SuperUserId}.");
|
||||
}
|
||||
//To change the password here we actually need to reset it since we don't have an old one to use to change
|
||||
var resetToken = await backOfficeUserManager.GeneratePasswordResetTokenAsync(membershipUser);
|
||||
if (string.IsNullOrWhiteSpace(resetToken))
|
||||
{
|
||||
throw new InvalidOperationException("Could not reset password: unable to generate internal reset token");
|
||||
}
|
||||
|
||||
//To change the password here we actually need to reset it since we don't have an old one to use to change
|
||||
var resetToken = await backOfficeUserManager.GeneratePasswordResetTokenAsync(membershipUser);
|
||||
if (string.IsNullOrWhiteSpace(resetToken))
|
||||
{
|
||||
throw new InvalidOperationException("Could not reset password: unable to generate internal reset token");
|
||||
}
|
||||
|
||||
IdentityResult resetResult = await backOfficeUserManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, unattendedPassword.Trim());
|
||||
if (!resetResult.Succeeded)
|
||||
{
|
||||
throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors.ToErrorMessage()));
|
||||
}
|
||||
IdentityResult resetResult = await backOfficeUserManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, unattendedPassword.Trim());
|
||||
if (!resetResult.Succeeded)
|
||||
{
|
||||
throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors.ToErrorMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user