Ntuthuko/consolidate authentication settings#4330
Ntuthuko/consolidate authentication settings#4330ntuthukomsane-boxfusion wants to merge 86 commits intoshesha-io:mainfrom
Conversation
…sha-framework into ntuthuko/consolidate-authentication-settings
…//github.com/ntuthukomsane-boxfusion/shesha-framework into ntuthuko/consolidate-authentication-settings
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughConsolidates frontend user/auth configuration into a new IUserManagementSettings surface, removes legacy settings types, rewires OTP/authorization/user services to use DefaultAuthentication/GeneralFrontendSecurity, refactors user-registration for dynamic Person resolution, adds frontend redirect settings, and includes a migration to transform stored settings. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant UserManagementAppService
participant EntityConfigStore
participant DynamicRepository
participant UserManager
participant RoleService
rect rgba(200,230,255,0.5)
Client->>UserManagementAppService: CreateAsync(registrationDto)
UserManagementAppService->>EntityConfigStore: Resolve Person entity type/config
EntityConfigStore-->>UserManagementAppService: Type metadata
UserManagementAppService->>DynamicRepository: FindOrCreatePerson(per CreationMode)
DynamicRepository-->>UserManagementAppService: Person entity
UserManagementAppService->>UserManager: CreateOrLinkUser(userDto, DefaultAuthentication)
UserManager-->>UserManagementAppService: User entity
UserManagementAppService->>RoleService: AssignDefaultRoles(user, config.DefaultRoles)
RoleService-->>UserManagementAppService: Roles assigned
UserManagementAppService-->>Client: Return mapped PersonAccountDto
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 26
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
shesha-core/src/Shesha.Application/Otp/OtpAppService.cs (1)
49-61: Same data loss risk: OTP update overwrites all other authentication settings.Similar to
AuthorizationSettingsAppService, this method creates a newDefaultAuthenticationSettingswith only OTP-related fields, overwriting any existing lockout, password complexity, and password reset settings configured elsewhere.This is especially problematic since both
OtpAppService.UpdateSettingsAsyncandAuthorizationSettingsAppService.UpdateSettingsAsyncwrite to the sameDefaultAuthenticationsettings object—whichever runs last will overwrite the other's fields.Apply the same fix: fetch existing settings first and preserve unrelated fields:
+ var existingSettings = await _userManagementSettings.DefaultAuthentication.GetValueAsync(); + await _userManagementSettings.DefaultAuthentication.SetValueAsync(new DefaultAuthenticationSettings { + // Preserve existing lockout, password complexity, and password reset settings + UserLockOutEnabled = existingSettings.UserLockOutEnabled, + MaxFailedAccessAttemptsBeforeLockout = existingSettings.MaxFailedAccessAttemptsBeforeLockout, + DefaultAccountLockoutSeconds = existingSettings.DefaultAccountLockoutSeconds, + RequireDigit = existingSettings.RequireDigit, + RequireLowercase = existingSettings.RequireLowercase, + RequireNonAlphanumeric = existingSettings.RequireNonAlphanumeric, + RequireUppercase = existingSettings.RequireUppercase, + RequiredLength = existingSettings.RequiredLength, + UseResetPasswordViaEmailLink = existingSettings.UseResetPasswordViaEmailLink, + ResetPasswordEmailLinkLifetime = existingSettings.ResetPasswordEmailLinkLifetime, + UseResetPasswordViaSmsOtp = existingSettings.UseResetPasswordViaSmsOtp, + ResetPasswordSmsOtpLifetime = existingSettings.ResetPasswordSmsOtpLifetime, + UseResetPasswordViaSecurityQuestions = existingSettings.UseResetPasswordViaSecurityQuestions, + ResetPasswordViaSecurityQuestionsNumQuestionsAllowed = existingSettings.ResetPasswordViaSecurityQuestionsNumQuestionsAllowed, + // ... other fields to preserve + PasswordLength = input.PasswordLength, Alphabet = input.Alphabet, ... });shesha-core/src/Shesha.Application/UserManagements/UserManagementAppService.cs (2)
77-205: Email is now effectively mandatory and domain validation throws non‑user‑friendly exceptionsWithin
CreateAsync,HandleAllowedDomainsAsyncis called unconditionally withinput.EmailAddress(line 153), and that helper throwsArgumentExceptionif the email is missing or malformed, andInvalidOperationExceptionif the domain is not allowed. This has a few implications:
- Behaviour change: even when
defaultAuthenticationSettings.UserEmailAsUsernameisfalse, email becomes mandatory becauseHandleAllowedDomainsAsyncwill throw for null/empty emails beforevalidationResults.ThrowValidationExceptionIfAny(L)runs.- Inconsistent UX: other validation errors are aggregated into
validationResultsand surfaced as proper validation exceptions, but domain issues surface asArgumentException/InvalidOperationException, which will typically present as 500s rather than user‑friendly messages.If email is meant to be optional when username is used, the domain handler should short‑circuit when
string.IsNullOrWhiteSpace(input.EmailAddress)andAllowedEmailDomainsis empty/whitespace. If email is meant to be required in all cases, consider:
- Expressing that via the
validationResultslist rather thanArgumentException.- Using
UserFriendlyException(or the same validation mechanism) for invalid format and disallowed domain to keep responses consistent.A possible direction (adjust to your conventions):
- await HandleAllowedDomainsAsync( - input.EmailAddress ?? throw new UserFriendlyException(nameof(input.EmailAddress), "Email address cannot be null."), - allowedEmailDomains ?? string.Empty - ); + if (!string.IsNullOrWhiteSpace(input.EmailAddress) || !string.IsNullOrWhiteSpace(allowedEmailDomains)) + { + await HandleAllowedDomainsAsync( + input.EmailAddress ?? throw new UserFriendlyException(nameof(input.EmailAddress), "Email address cannot be null."), + allowedEmailDomains ?? string.Empty + ); + }and inside
HandleAllowedDomainsAsync(see separate comment) change the thrown exception types to your user‑facing ones.
162-318: CreationMode handling is inconsistent (null CreationMode and MustAlreadyExist with user‑less Person will currently blow up)The combination of
HandleCreationModeAsync(lines 243–270) andCreateOrLinkUserAsync(lines 272–318) has a couple of problematic edge cases:
creationMode == null
HandleCreationModeAsynchits thedefaultbranch and throwsNotImplementedExceptioneven though the comment says “Default behavior (no creation mode specified) – create new person.”CreateOrLinkUserAsyncalso throwsArgumentNullExceptionwhencreationModeis null (line 280), whileCreateOrLinkPersonEntityAsync’sdefaultcase happily creates a new person (lines 357–360).
This will break existing tenants whereCreationModehasn’t been configured yet and also contradicts the documented “default” behaviour.
RefListCreationMode.MustAlreadyExistwith an existingPersonbut noPerson.User
HandleCreationModeAsynconly requiresexistingPerson != null(lines 257–260), so validation passes.- In
CreateOrLinkUserAsync,foundUserbecomes((Person)existingPerson).User. If that property is null, there is no matching switch arm forMustAlreadyExist, so thedefaultarm runs and throwsInvalidOperationException("Unsupported creation mode: ...")(line 316).- Business‑wise,
MustAlreadyExistusually means “person record must already exist”; it does not require a pre‑existingUser. You almost certainly want to allow creating a new user and then linking it.To make behaviour consistent and keep backwards compatibility, I suggest:
- Treat
nullCreationModeas the legacy “create new person + user” behaviour.- For
MustAlreadyExist, allow the case where the person exists but has no user: create a new user and then link.One possible refactor:
- private static async Task HandleCreationModeAsync(RefListCreationMode? creationMode, object? existingPerson) - { - await Task.CompletedTask; // Make it async compatible since we no longer need to call database - - switch (creationMode) - { - case RefListCreationMode.Always: - if (existingPerson != null) - throw new UserFriendlyException($"A person with the same identifier already exists. Creation mode is set to 'Always' which requires no existing person."); - break; - - case RefListCreationMode.MustAlreadyExist: - if (existingPerson == null) - throw new UserFriendlyException($"No existing person found with the specified identifier. Creation mode is set to 'Must already exist' which requires a pre-existing person."); - break; - - case RefListCreationMode.CreateNewButLinkIfExist: - // No validation needed - handled in CreateOrLinkPersonAsync - break; - - default: - // Default behavior (no creation mode specified) - create new person - throw new NotImplementedException(); - } - } + private static Task HandleCreationModeAsync(RefListCreationMode? creationMode, object? existingPerson) + { + // Default behaviour when not configured: no extra validation (equivalent to "create new") + if (creationMode == null) + return Task.CompletedTask; + + switch (creationMode) + { + case RefListCreationMode.Always: + if (existingPerson != null) + throw new UserFriendlyException("A person with the same identifier already exists. Creation mode is set to 'Always' which requires no existing person."); + break; + + case RefListCreationMode.MustAlreadyExist: + if (existingPerson == null) + throw new UserFriendlyException("No existing person found with the specified identifier. Creation mode is set to 'Must already exist' which requires a pre-existing person."); + break; + + case RefListCreationMode.CreateNewButLinkIfExist: + // No additional validation – linking logic handles this mode. + break; + + default: + throw new InvalidOperationException($"Unsupported creation mode: {creationMode}"); + } + + return Task.CompletedTask; + }and in
CreateOrLinkUserAsync:- private async Task<User> CreateOrLinkUserAsync( + private async Task<User> CreateOrLinkUserAsync( RefListCreationMode? creationMode, object? existingPerson, CreatePersonAccountDto input, string? userName, long? supportedPasswordResetMethods ) { - if (creationMode is null) - throw new ArgumentNullException(nameof(creationMode)); - var isInternalAccount = input.TypeOfAccount?.ItemValue == (long)RefListTypeOfAccount.Internal; var username = userName.NotNull(); var foundUser = existingPerson != null ? ((Person)existingPerson).User : null; @@ - return creationMode switch - { - RefListCreationMode.Always when foundUser is null => await CreateUserAsync(), - RefListCreationMode.MustAlreadyExist when foundUser is not null => await LinkUserAsync(), - RefListCreationMode.CreateNewButLinkIfExist when foundUser is not null => await LinkUserAsync(), - RefListCreationMode.CreateNewButLinkIfExist => await CreateUserAsync(), - _ => throw new InvalidOperationException($"Unsupported creation mode: {creationMode}") - }; + if (creationMode is null) + { + // Legacy/default behaviour: always create a new user + return await CreateUserAsync(); + } + + return creationMode switch + { + RefListCreationMode.Always => await CreateUserAsync(), + + // Person must exist by now; if they already have a user, link, otherwise create a new one. + RefListCreationMode.MustAlreadyExist when foundUser is not null => await LinkUserAsync(), + RefListCreationMode.MustAlreadyExist => await CreateUserAsync(), + + RefListCreationMode.CreateNewButLinkIfExist when foundUser is not null => await LinkUserAsync(), + RefListCreationMode.CreateNewButLinkIfExist => await CreateUserAsync(), + + _ => throw new InvalidOperationException($"Unsupported creation mode: {creationMode}") + }; }This keeps
CreateOrLinkPersonEntityAsync’s default behaviour (“create new person”) aligned with the user creation logic and preventsNotImplementedException/InvalidOperationExceptionfrom leaking to callers in normal configuration scenarios.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
⛔ Files ignored due to path filters (2)
shesha-core/test/Shesha.Tests/Otp/OtpManager_Tests.csis excluded by none and included by noneshesha-core/test/Shesha.Tests/Otp/PasswordGenerator_Tests.csis excluded by none and included by none
📒 Files selected for processing (33)
shesha-core/src/Shesha.Application/Authorization/Settings/AuthorizationSettingsAppService.cs(1 hunks)shesha-core/src/Shesha.Application/Authorization/Settings/Dto/AuthorizationSettingsDto.cs(3 hunks)shesha-core/src/Shesha.Application/Notifications/NotificationTemplateProcessor.cs(1 hunks)shesha-core/src/Shesha.Application/Otp/Configuration/IOtpSettings.cs(0 hunks)shesha-core/src/Shesha.Application/Otp/Configuration/OtpSettings.cs(0 hunks)shesha-core/src/Shesha.Application/Otp/OtpAppService.cs(3 hunks)shesha-core/src/Shesha.Application/Otp/OtpGenerator.cs(1 hunks)shesha-core/src/Shesha.Application/Otp/OtpManager.cs(6 hunks)shesha-core/src/Shesha.Application/Persons/PersonAccountDto.cs(0 hunks)shesha-core/src/Shesha.Application/SecurityQuestions/QuestionAnswersAppService.cs(3 hunks)shesha-core/src/Shesha.Application/ShaRoleAppointedPersons/Dto/ShaRoleAppointedPersonMapProfile.cs(1 hunks)shesha-core/src/Shesha.Application/Shesha.Application.csproj(2 hunks)shesha-core/src/Shesha.Application/SheshaApplicationModule.cs(4 hunks)shesha-core/src/Shesha.Application/UserManagements/Configurations/IUserManagementSettings.cs(0 hunks)shesha-core/src/Shesha.Application/UserManagements/Configurations/UserManagementSettingNames.cs(0 hunks)shesha-core/src/Shesha.Application/UserManagements/Configurations/UserManagementSettings.cs(0 hunks)shesha-core/src/Shesha.Application/UserManagements/UserManagementAppService.cs(3 hunks)shesha-core/src/Shesha.Application/Users/UserAppService.cs(11 hunks)shesha-core/src/Shesha.Framework/Authorization/Users/UserManager.cs(6 hunks)shesha-core/src/Shesha.Framework/Configuration/FrontendApplicationRedirectsSettings.cs(1 hunks)shesha-core/src/Shesha.Framework/Configuration/IFrontendSettings.cs(1 hunks)shesha-core/src/Shesha.Framework/Configuration/IPasswordComplexitySettings.cs(0 hunks)shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/DefaultAuthenticationSettings.cs(1 hunks)shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/GeneralFrontendSecuritySettings.cs(1 hunks)shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/IUserManagementSettings.cs(1 hunks)shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/RefListCreationMode.cs(1 hunks)shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/UserManagementSettingNames.cs(1 hunks)shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/UserManagementSettings.cs(1 hunks)shesha-core/src/Shesha.Framework/Configuration/Security/ISecuritySettings.cs(0 hunks)shesha-core/src/Shesha.Framework/Configuration/Security/SecuritySettings.cs(0 hunks)shesha-core/src/Shesha.Framework/Configuration/SheshaSettingNames.cs(1 hunks)shesha-core/src/Shesha.Framework/Domain/Enums/SupportedRegistrationMethods.cs(1 hunks)shesha-core/src/Shesha.Framework/SheshaFrameworkModule.cs(3 hunks)
💤 Files with no reviewable changes (9)
- shesha-core/src/Shesha.Application/UserManagements/Configurations/UserManagementSettings.cs
- shesha-core/src/Shesha.Framework/Configuration/IPasswordComplexitySettings.cs
- shesha-core/src/Shesha.Application/Persons/PersonAccountDto.cs
- shesha-core/src/Shesha.Application/UserManagements/Configurations/IUserManagementSettings.cs
- shesha-core/src/Shesha.Application/UserManagements/Configurations/UserManagementSettingNames.cs
- shesha-core/src/Shesha.Framework/Configuration/Security/ISecuritySettings.cs
- shesha-core/src/Shesha.Framework/Configuration/Security/SecuritySettings.cs
- shesha-core/src/Shesha.Application/Otp/Configuration/IOtpSettings.cs
- shesha-core/src/Shesha.Application/Otp/Configuration/OtpSettings.cs
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-05T17:11:17.267Z
Learnt from: teboho
Repo: shesha-io/shesha-framework PR: 3678
File: shesha-core/src/Shesha.Application/Otp/Configuration/OtpDefaults.cs:14-15
Timestamp: 2025-08-05T17:11:17.267Z
Learning: In the Shesha framework, the DefaultEmailBodyTemplate in OtpDefaults.cs contains a hard-coded localhost URL by design. This serves as a default that gets updated/overridden in production environments through their existing configuration management process.
Applied to files:
shesha-core/src/Shesha.Application/Notifications/NotificationTemplateProcessor.csshesha-core/src/Shesha.Application/Otp/OtpGenerator.csshesha-core/src/Shesha.Application/Otp/OtpManager.csshesha-core/src/Shesha.Application/Otp/OtpAppService.csshesha-core/src/Shesha.Framework/Configuration/SheshaSettingNames.csshesha-core/src/Shesha.Framework/SheshaFrameworkModule.csshesha-core/src/Shesha.Framework/Configuration/Security/Frontend/DefaultAuthenticationSettings.csshesha-core/src/Shesha.Application/SheshaApplicationModule.cs
📚 Learning: 2025-10-25T09:24:59.417Z
Learnt from: teboho
Repo: shesha-io/shesha-framework PR: 4063
File: shesha-core/src/Shesha.Web.Host/appsettings.Docker.json:3-3
Timestamp: 2025-10-25T09:24:59.417Z
Learning: In the shesha-framework repository, for Docker-specific configuration files like appsettings.Docker.json used for local development, hardcoded credentials are acceptable for plug-and-play developer experience.
Applied to files:
shesha-core/src/Shesha.Framework/SheshaFrameworkModule.cs
🔇 Additional comments (26)
shesha-core/src/Shesha.Application/Authorization/Settings/Dto/AuthorizationSettingsDto.cs (1)
67-70: LGTM!The new
AutoLogoffAfterInactivityboolean property complements the existingAutoLogoffTimeoutinteger, providing a clear toggle for the auto-logoff feature.shesha-core/src/Shesha.Application/SheshaApplicationModule.cs (1)
154-154: LGTM!The service registration follows the existing patterns and uses the appropriate transient lifecycle.
shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/RefListCreationMode.cs (1)
12-38: LGTM!The enum is well-designed with clear documentation for each creation mode. The
ReferenceListattribute properly registers it for framework use.shesha-core/src/Shesha.Framework/Domain/Enums/SupportedRegistrationMethods.cs (3)
17-17: LGTM - Clarified display name.The display name change from "Email Address" to "Email OTP" better clarifies the registration method involves OTP verification, improving user understanding.
22-22: LGTM - Clarified display name.The display name change from "Mobile Number" to "Mobile OTP" better clarifies the registration method involves OTP verification, improving user understanding.
29-33: LGTM - New registration method added.The addition of
EmailLink = 5provides an alternative email-based registration method using verification links instead of OTP codes, expanding the available authentication options.shesha-core/src/Shesha.Application/Otp/OtpGenerator.cs (1)
10-15: LGTM - Dependency updated to consolidated settings.The constructor correctly migrates from
IOtpSettingstoIUserManagementSettings, aligning with the PR's consolidation objective.shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/UserManagementSettingNames.cs (1)
1-9: LGTM - Clean constants definition.The
UserManagementSettingNamesclass provides standardized setting name keys for the consolidated user management configuration surface. The implementation is clean and follows established patterns.shesha-core/src/Shesha.Application/Otp/OtpManager.cs (5)
21-30: LGTM - Dependency updated to consolidated settings.The constructor correctly migrates from
IOtpSettingstoIUserManagementSettings, maintaining the same functionality while aligning with the PR's consolidation objective.
40-40: LGTM - Settings retrieval path updated.The settings retrieval correctly uses
_userManagementSettings.DefaultAuthentication.GetValueAsync(), consistent with the configuration consolidation.
93-93: LGTM - Settings retrieval path updated.The settings retrieval correctly uses
_userManagementSettings.DefaultAuthentication.GetValueAsync(), consistent with the configuration consolidation.
165-165: LGTM - Settings retrieval path updated.The settings retrieval correctly uses
_userManagementSettings.DefaultAuthentication.GetValueAsync(), consistent with the configuration consolidation.
189-189: LGTM - Settings retrieval path updated.The settings retrieval correctly uses
_userManagementSettings.DefaultAuthentication.GetValueAsync(), consistent with the configuration consolidation.shesha-core/src/Shesha.Application/SecurityQuestions/QuestionAnswersAppService.cs (2)
38-38: [rewritten comment]
[classification tag]
18-26: LGTM - Dependency updated to consolidated settings.The service shows correct injection of
IUserManagementSettingsin the constructor. However, verification of theResetPasswordViaSecurityQuestionsNumQuestionsAllowedproperty availability in the migrated settings could not be completed due to repository access limitations.shesha-core/src/Shesha.Application/Notifications/NotificationTemplateProcessor.cs (1)
97-104: Breaking change: Property type changed from string to complex object.The
PublicUrlproperty now returnsFrontendApplicationRedirectsSettings?instead ofstring?, which is a breaking change for any mustache templates or code that accesses this property. Template expressions like{{frontEndApps.myApp.PublicUrl}}will now render an object representation instead of a URL string.Consider either:
- Renaming the property to
ApplicationRedirectsSettingsto reflect its actual type, OR- Adding a separate
BaseUrlproperty that returnsstring?for backward compatibilityVerify that all mustache templates and notification templates using
PublicUrlare updated to access the nested properties (e.g.,{{frontEndApps.myApp.PublicUrl.BaseUrl}}), or confirm that backward compatibility measures were implemented.shesha-core/src/Shesha.Framework/Configuration/IFrontendSettings.cs (1)
23-27: Unable to verify breaking change claim due to repository access limitations.The breaking change assertion about removal of
DefaultUrlandPublicUrlproperties cannot be verified without access to the repository history and codebase search capabilities. To properly assess this issue, provide:
- Git commit or diff showing the removed properties from
IFrontendSettings.cs- Confirmation that these properties existed in the previous version
- Evidence of where references to these properties have been migrated or updated
shesha-core/src/Shesha.Framework/Configuration/SheshaSettingNames.cs (2)
25-29: LGTM! New setting name constants added.The new constants
SuccessLoginRedirectPath,ApplicationRedirects, andDefaultAuthenticationSettingsfollow the established naming conventions and support the authentication settings consolidation.
19-21: Verify breaking change: renamed setting name constants.Renaming
DefaultUrl→BaseUrlandPublicUrl→DefaultPathchanges the underlying string values from"Shesha.DefaultUrl"to"Shesha.BaseUrl"and"Shesha.PublicUrl"to"Shesha.DefaultPath". This may affect persisted settings or external consumers referencing the old names. Ensure any config migrations or deprecation handling is in place.shesha-core/src/Shesha.Application/Users/UserAppService.cs (2)
56-83: LGTM! Dependency injection properly wired.The
IUserManagementSettingsis correctly injected and stored in a private field, following the established pattern.
289-304: LGTM! Settings access migrated correctly.The retrieval of authentication settings from
_userManagementSettings.DefaultAuthenticationand subsequent use for feature toggles is correctly implemented.shesha-core/src/Shesha.Framework/SheshaFrameworkModule.cs (2)
131-136: LGTM! Security settings simplified.The
SecuritySettingsdefault now only configuresDefaultEndpointAccess, which aligns with moving password/lockout settings to the consolidatedIUserManagementSettingsinterface.
144-149: Consider addingDefaultPathto the default initialization.
FrontendApplicationRedirectsSettingshas four properties, but only three are initialized here. TheDefaultPathproperty is missing from the defaults. If it's intentionally omitted, this is fine; otherwise, consider adding a sensible default.shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/IUserManagementSettings.cs (1)
7-30: Well-structured settings interface consolidation.The interface cleanly consolidates authentication-related settings into a single access point. The use of
IsClientSpecific = trueon all settings is appropriate for tenant-specific configurations, and the editor form names follow a consistent naming convention.shesha-core/src/Shesha.Framework/Authorization/Users/UserManager.cs (1)
351-368: LGTM for async initialization.The async version correctly reads settings from
DefaultAuthenticationand applies appropriate null-coalescing defaults for lockout and password complexity options.shesha-core/src/Shesha.Application/UserManagements/UserManagementAppService.cs (1)
487-523: Dynamic default role assignment logic looks sound; just be mindful of potential duplicate role assignmentsThe
AssignDefaultRolesAsynchelper correctly:
- Extracts the person
Idvia reflection, which works for anyPerson‑derived dynamic type.- Skips null/empty role ids.
- Checks
_roleAppointedPersonRepositoryfor an existing appointment before calling_roleAppointedPersonService.CreateAsync, avoiding duplicates.This is generally solid. Two minor considerations:
- If
_roleAppointedPersonService.CreateAsynchas side effects or heavy logic, you might want to refactor to a batch‑oriented service in future, but that’s an optimisation, not a blocker here.- If there is a unique constraint at the database level on
(RoleId, PersonId), this pattern is safe from race conditions in single‑request flows but can still theoretically race under concurrent registrations for the same person; the DB constraint would be the final guard.No changes strictly required now; just keep these aspects in mind.
…nd/RefListCreationMode.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…nd/UserManagementSettings.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…nd/UserManagementSettings.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…AuthorizationSettingsDto.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…sha-framework into ntuthuko/consolidate-authentication-settings
…//github.com/ntuthukomsane-boxfusion/shesha-framework into ntuthuko/consolidate-authentication-settings
…sha-framework into ntuthuko/consolidate-authentication-settings
…//github.com/ntuthukomsane-boxfusion/shesha-framework into ntuthuko/consolidate-authentication-settings
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/DefaultAuthenticationSettings.cs (2)
30-34: Empty XML documentation comment forUserEmailAsUsername.The
<summary>tag is empty. Add a meaningful description./// <summary> - /// + /// Specifies whether the user's email address should be used as their username. /// </summary> [Display(Name = "Use email as username")] public bool UserEmailAsUsername { get; set; }
106-137: Non-nullable string properties may causeNullReferenceException.
Alphabet,DefaultSubjectTemplate,DefaultBodyTemplate,DefaultEmailSubjectTemplate, andDefaultEmailBodyTemplateare declared as non-nullablestringbut have no default values. When settings are deserialized without these values, they will benull, potentially causing NREs.Either make them nullable or provide default values:
- public string Alphabet { get; set; } + public string Alphabet { get; set; } = string.Empty; ... - public string DefaultSubjectTemplate { get; set; } + public string DefaultSubjectTemplate { get; set; } = string.Empty; ... - public string DefaultBodyTemplate { get; set; } + public string DefaultBodyTemplate { get; set; } = string.Empty; ... - public string DefaultEmailSubjectTemplate { get; set; } + public string DefaultEmailSubjectTemplate { get; set; } = string.Empty; ... - public string DefaultEmailBodyTemplate { get; set; } + public string DefaultEmailBodyTemplate { get; set; } = string.Empty;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (6)
shesha-core/src/Shesha.Application/Authorization/Settings/AuthorizationSettingsAppService.cs(1 hunks)shesha-core/src/Shesha.Application/Authorization/Settings/Dto/AuthorizationSettingsDto.cs(3 hunks)shesha-core/src/Shesha.Application/Otp/OtpGenerator.cs(1 hunks)shesha-core/src/Shesha.Application/ShaRoleAppointedPersons/Dto/ShaRoleAppointedPersonMapProfile.cs(1 hunks)shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/DefaultAuthenticationSettings.cs(1 hunks)shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/UserManagementSettings.cs(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-05T17:11:17.267Z
Learnt from: teboho
Repo: shesha-io/shesha-framework PR: 3678
File: shesha-core/src/Shesha.Application/Otp/Configuration/OtpDefaults.cs:14-15
Timestamp: 2025-08-05T17:11:17.267Z
Learning: In the Shesha framework, the DefaultEmailBodyTemplate in OtpDefaults.cs contains a hard-coded localhost URL by design. This serves as a default that gets updated/overridden in production environments through their existing configuration management process.
Applied to files:
shesha-core/src/Shesha.Application/Otp/OtpGenerator.csshesha-core/src/Shesha.Framework/Configuration/Security/Frontend/DefaultAuthenticationSettings.cs
🔇 Additional comments (4)
shesha-core/src/Shesha.Application/ShaRoleAppointedPersons/Dto/ShaRoleAppointedPersonMapProfile.cs (1)
13-17: RoleId may no longer be explicitly mapped on the DTOThe current
CreateMap<ShaRoleAppointedPerson, ShaRoleAppointedPersonDto>()only configuresPerson. If the previous version explicitly mappedRoleIdfrome.Role.Id, this mapping is no longer present. Verify whether:
ShaRoleAppointedPersonDtohas aRoleIdproperty and whether it is still being populatedShaRoleAppointedPersonexposesRolenavigation (so AutoMapper flattening could apply) or only a scalarRoleId- Any code depends on
RoleIdbeing populated on the DTOIf consumers require
RoleIdand it is not being auto-mapped via conventions, restore the mapping alongside thePersonconfiguration.shesha-core/src/Shesha.Framework/Configuration/Security/Frontend/UserManagementSettings.cs (1)
9-48: LGTM! All previous review comments have been addressed.The class is well-structured with proper XML documentation, nullable annotations where appropriate, and default initialization for
DefaultRoles. Good use ofDisplayattributes for UI binding.shesha-core/src/Shesha.Application/Otp/OtpGenerator.cs (1)
17-31: LGTM! Previous optimization feedback addressed.The settings are now retrieved once and cached in
authSettings. Good use ofStringBuilderfor efficient PIN generation.Note: Consider adding a null/empty guard for
alphabetto preventIndexOutOfRangeExceptionif the setting is not configured:var authSettings = _userManagementSettings.DefaultAuthentication.GetValue(); var alphabet = authSettings.Alphabet; var passwordLength = authSettings.PasswordLength; + + if (string.IsNullOrEmpty(alphabet)) + throw new InvalidOperationException("OTP alphabet is not configured."); for (int i = 0; i < passwordLength; i++)shesha-core/src/Shesha.Application/Authorization/Settings/Dto/AuthorizationSettingsDto.cs (1)
1-96: LGTM!The DTO is well-structured with proper XML documentation. The new
AutoLogoffAfterInactivityproperty andDisplayattributes are appropriately added. Previous feedback about unused imports has been addressed.
There was a problem hiding this comment.
Actionable comments posted: 4
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (2)
shesha-core/src/Shesha.Application/Authorization/Settings/AuthorizationSettingsAppService.cs(1 hunks)shesha-core/src/Shesha.Application/ShaRoleAppointedPersons/Dto/ShaRoleAppointedPersonMapProfile.cs(1 hunks)
🔇 Additional comments (2)
shesha-core/src/Shesha.Application/ShaRoleAppointedPersons/Dto/ShaRoleAppointedPersonMapProfile.cs (1)
23-27: LGTM!The Entity→DTO mapping correctly transforms the
Personentity to anEntityReferenceDto<Guid?>with display name and type information. The null check is appropriate.shesha-core/src/Shesha.Application/Authorization/Settings/AuthorizationSettingsAppService.cs (1)
64-68: Verify whetherGeneralFrontendSecuritySettingshas other properties that should be preserved.This code creates a new
GeneralFrontendSecuritySettingsobject with only two properties from the DTO (AutoLogoffAfterInactivityandAutoLogoffTimeout). IfGeneralFrontendSecuritySettingshas additional properties not included here, they will be lost when this object is saved. Check the class definition to confirm all properties are being set, or consider using a merge/update pattern similar to the fix applied toDefaultAuthenticationSettings.
…orizationSettingsAppService.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…tionRedirectsSettings.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@shesha-core/src/Shesha.Framework/Configuration/FrontendApplicationRedirectsSettings.cs`:
- Line 32: Update the Display attribute description to fix the typo: change
"authentications" to "authentication" in the Description string on the Display
attribute applied to the property annotated with Name = "Not authenticated
redirect path" in FrontendApplicationRedirectsSettings (the Display attribute
contains the Description to update).
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@shesha-core/src/Shesha.Framework/Authorization/Users/UserManager.cs`:
- Around line 287-293: The code adds a ValidationResult and immediately calls
validationResults.ThrowValidationExceptionIfAny(existing logger L), so the
subsequent throw new InvalidOperationException(message) is unreachable; remove
that redundant throw (delete the throw new InvalidOperationException(message)
line) or, if you want an explicit safety net, replace it with a clear comment or
a Debug/Trace.Fail call after ThrowValidationExceptionIfAny to document the
intent; locate symbols: existingUser, validationResults,
ThrowValidationExceptionIfAny and message in UserManager.cs to apply the change.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@shesha-core/src/Shesha.Framework/Migrations/M20251215130900.cs`:
- Around line 14-316: The migration currently transforms Shesha.UserManagement
(user_mgmt_cursor) and Shesha.Security (security_cursor) before processing
OTP/DefaultAuthentication (otp_cursor), which causes fields like
requireEmailVerification, userEmailAsUsername, supportedRegistrationMethods and
resetPassword* to be lost; fix by reordering the SQL blocks so the otp_cursor
section (Step 3 that reads `@OldOtpValue`, `@ContextUserManagementValue`,
`@ContextSecurityValue` and builds `@NewDefaultAuthValue` / inserts/updates
DefaultAuthenticationSettings) runs before the user_mgmt_cursor (Step 1 that
updates Shesha.UserManagement) and security_cursor (Step 2 that updates
Shesha.Security), and apply the same reorder in the PostgreSQL branch so
otp-based merging reads original JSON values rather than the already-transformed
structures.
- Around line 84-91: The variables `@AutoLogoffTimeout`,
`@UseResetPasswordViaEmailLink`, `@ResetPasswordEmailLinkLifetime`,
`@UseResetPasswordViaSmsOtp`, `@ResetPasswordSmsOtpLifetime`,
`@UseResetPasswordViaSecurityQuestions` and
`@ResetPasswordViaSecurityQuestionsNumQuestionsAllowed` are declared in the outer
scope but only assigned inside the security cursor and never used after the
cursor closes; remove these outer declarations and instead declare any needed
copies inside the cursor loop (or inline the assignments into loop-local
variables) so there are no dead outer-scope variables, and delete or refactor
the assignments on lines that set those names (currently in the cursor body) to
use the new loop-local variable names.
…nd/DefaultAuthenticationSettings.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…entAppService.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…//github.com/ntuthukomsane-boxfusion/shesha-framework into ntuthuko/consolidate-authentication-settings
…tionRedirectsSettings.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…er.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…//github.com/ntuthukomsane-boxfusion/shesha-framework into ntuthuko/consolidate-authentication-settings
IvanIlyichev
left a comment
There was a problem hiding this comment.
Hi @ntuthukomsane-boxfusion. Please address the following:
- Review all inline comments.
- Ensure settings are properly organized and behave as intended. For example:
Enable authentication with local username/password?is disabled by default and not actually used by the application.- It is unclear why this setting is placed under Account creation.
Account creationandNew User Registrationappear to be redundant. These sections should likely be merged and moved outside ofAuthentication.- The
Generalgroup currently contains only session-related settings; renaming it toUser Sessionwould be more appropriate.
The current structure of the Default Authentication settings group mirrors the layout of the login form, which is not a logical approach. Instead, we should extract distinct functional blocks. This will allow for more graceful extensibility and result in a more intuitive UI for administrators. For instance, OTP functionality—which is expected to be enhanced and widely used across various modules for confirmations—should be treated as a standalone block.
…ment from IUserManagementSettings to their own interface
…//github.com/ntuthukomsane-boxfusion/shesha-framework into ntuthuko/consolidate-authentication-settings
This pr addresses the following:
[#4366]
Consolidation of authentication settings by taking all the authentication settings which were previously used as individual settings into a newly created "DefaultAuthenticationSettings" object
Summary by CodeRabbit
New Features
Updates
Style
Chores