diff --git a/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/IPasswordRuleProvider.cs b/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/IPasswordRuleProvider.cs index 0f41cdce..b0f558e6 100644 --- a/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/IPasswordRuleProvider.cs +++ b/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/IPasswordRuleProvider.cs @@ -8,7 +8,7 @@ namespace Masa.Auth.Contracts.Admin.Infrastructure.Password; /// A client-level rule (stored on ClientConfig) takes precedence; when absent it falls back to /// the global DCC configuration. /// -public interface IPasswordRuleProvider +public interface IPasswordRuleProvider : IScopedDependency { /// /// Validate a password against the client's effective rule. @@ -16,6 +16,8 @@ public interface IPasswordRuleProvider /// Designed to be called from a FluentValidation rule (see ) /// so client-aware password validation stays inside the single, framework-invoked validation pipeline. /// + string? GetFailure(string? password, string? clientId); + Task GetFailureAsync(string? password, string? clientId); /// diff --git a/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/PasswordRuleValidatorExtensions.cs b/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/PasswordRuleValidatorExtensions.cs index e3da97f2..1a36430e 100644 --- a/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/PasswordRuleValidatorExtensions.cs +++ b/src/Contracts/Masa.Auth.Contracts.Admin/Infrastructure/Password/PasswordRuleValidatorExtensions.cs @@ -15,9 +15,9 @@ public static class PasswordRuleValidatorExtensions IPasswordRuleProvider passwordRuleProvider, Func clientId) { - return ruleBuilder.CustomAsync(async (password, context, cancellation) => + return ruleBuilder.Custom((password, context) => { - var failure = await passwordRuleProvider.GetFailureAsync(password, clientId(context.InstanceToValidate)); + var failure = passwordRuleProvider.GetFailure(password, clientId(context.InstanceToValidate)); if (failure is not null) { context.AddFailure(failure); diff --git a/src/Services/Masa.Auth.Service.Admin/Application/Subjects/Commands/ResetPasswordCommandValidator.cs b/src/Services/Masa.Auth.Service.Admin/Application/Subjects/Commands/ResetPasswordCommandValidator.cs index 2a6163f3..f9c9beb6 100644 --- a/src/Services/Masa.Auth.Service.Admin/Application/Subjects/Commands/ResetPasswordCommandValidator.cs +++ b/src/Services/Masa.Auth.Service.Admin/Application/Subjects/Commands/ResetPasswordCommandValidator.cs @@ -16,8 +16,6 @@ public ResetPasswordCommandValidator(PhoneNumberValidator phoneValidator, IPassw RuleFor(x => x.Voucher).Required().Email(); }); RuleFor(x => x.Captcha).Required(); - // Anonymous forgot-password flow (no login) - the client rule follows whatever ClientId - // the caller passes for this request; falls back to the global DCC rule when absent. RuleFor(x => x.Password).PasswordRule(passwordRuleProvider, x => x.ClientId); RuleFor(x => x.ConfirmPassword).Equal(x => x.Password); } diff --git a/src/Services/Masa.Auth.Service.Admin/Infrastructure/Message/ClientMessageTemplateProvider.cs b/src/Services/Masa.Auth.Service.Admin/Infrastructure/Message/ClientMessageTemplateProvider.cs index 6f27dc5b..1cb22f8f 100644 --- a/src/Services/Masa.Auth.Service.Admin/Infrastructure/Message/ClientMessageTemplateProvider.cs +++ b/src/Services/Masa.Auth.Service.Admin/Infrastructure/Message/ClientMessageTemplateProvider.cs @@ -3,7 +3,7 @@ namespace Masa.Auth.Service.Admin.Infrastructure.Message; -public class ClientMessageTemplateProvider : IClientMessageTemplateProvider, IScopedDependency +public class ClientMessageTemplateProvider : IClientMessageTemplateProvider { private readonly IClientConfigRepository _clientConfigRepository; diff --git a/src/Services/Masa.Auth.Service.Admin/Infrastructure/Message/IClientMessageTemplateProvider.cs b/src/Services/Masa.Auth.Service.Admin/Infrastructure/Message/IClientMessageTemplateProvider.cs index f461dcd8..807a3647 100644 --- a/src/Services/Masa.Auth.Service.Admin/Infrastructure/Message/IClientMessageTemplateProvider.cs +++ b/src/Services/Masa.Auth.Service.Admin/Infrastructure/Message/IClientMessageTemplateProvider.cs @@ -8,7 +8,7 @@ namespace Masa.Auth.Service.Admin.Infrastructure.Message; /// (channel type, scene). Returns null when the client has no matching configuration, /// signalling the caller to fall back to the global DCC SmsOptions/EmailOptions. /// -public interface IClientMessageTemplateProvider +public interface IClientMessageTemplateProvider : IScopedDependency { Task<(string channelCode, string templateCode)?> ResolveAsync(string? clientId, ChannelTypes channelType, int scene); } diff --git a/src/Services/Masa.Auth.Service.Admin/Infrastructure/Password/PasswordRuleProvider.cs b/src/Services/Masa.Auth.Service.Admin/Infrastructure/Password/PasswordRuleProvider.cs index dc4a372a..b3e21c64 100644 --- a/src/Services/Masa.Auth.Service.Admin/Infrastructure/Password/PasswordRuleProvider.cs +++ b/src/Services/Masa.Auth.Service.Admin/Infrastructure/Password/PasswordRuleProvider.cs @@ -5,7 +5,7 @@ namespace Masa.Auth.Service.Admin.Infrastructure.Password; -public class PasswordRuleProvider : IPasswordRuleProvider, IScopedDependency +public class PasswordRuleProvider : IPasswordRuleProvider { public const string PASSWORDRULECONFIGNAME = "$public.AppSettings:PasswordRule"; public const string PASSWORDPROMPTCONFIGNAME = "$public.AppSettings:PasswordPrompt"; @@ -26,6 +26,12 @@ public PasswordRuleProvider( _logger = logger; } + public string? GetFailure(string? password, string? clientId) + { + var (rule, prompt) = GetEffectiveRule(clientId); + return ValidatePasswordWithRule(password, rule, prompt); + } + public async Task GetFailureAsync(string? password, string? clientId) { var (rule, prompt) = await GetEffectiveRuleAsync(clientId); @@ -38,6 +44,26 @@ public Task GenerateNewPasswordAsync() return Task.FromResult(GenerateNewPassword(passwordRule)); } + private (string rule, string prompt) GetEffectiveRule(string? clientId) + { + var (rule, prompt) = GetGlobalPasswordRule(); + + if (!string.IsNullOrEmpty(clientId)) + { + var clientConfig = _clientConfigRepository.FindByClientIdAsync(clientId).Result; + if (clientConfig is not null && !string.IsNullOrEmpty(clientConfig.PasswordRule)) + { + rule = clientConfig.PasswordRule; + if (!string.IsNullOrEmpty(clientConfig.PasswordPrompt)) + { + prompt = clientConfig.PasswordPrompt; + } + } + } + + return (rule, prompt); + } + private async Task<(string rule, string prompt)> GetEffectiveRuleAsync(string? clientId) { // 全局 DCC 配置作为回退默认值 diff --git a/src/Services/Masa.Auth.Service.Admin/Program.cs b/src/Services/Masa.Auth.Service.Admin/Program.cs index bc0e95a6..64256bb1 100644 --- a/src/Services/Masa.Auth.Service.Admin/Program.cs +++ b/src/Services/Masa.Auth.Service.Admin/Program.cs @@ -18,7 +18,6 @@ #endif builder.Services.AddAutoInject(); -builder.Services.AddScoped(); builder.Services.AddDaprClient(); builder.Services.AddObjectStorage(option => option.UseAliyunStorage()); diff --git a/src/Services/Masa.Auth.Service.Admin/_Imports.cs b/src/Services/Masa.Auth.Service.Admin/_Imports.cs index 072b1ac4..9f0b92f6 100644 --- a/src/Services/Masa.Auth.Service.Admin/_Imports.cs +++ b/src/Services/Masa.Auth.Service.Admin/_Imports.cs @@ -85,6 +85,7 @@ global using Masa.Auth.Service.Admin.Infrastructure.Message; global using Masa.Auth.Service.Admin.Infrastructure.Middleware; global using Masa.Auth.Service.Admin.Infrastructure.Models; +global using Masa.Auth.Service.Admin.Infrastructure.Password; global using Masa.Auth.Service.Admin.Infrastructure.Sms; global using Masa.Auth.Service.Admin.Infrastructure.Utils; global using Masa.BuildingBlocks.Authentication.Identity;