diff --git a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor index 28166ecb..54b53348 100644 --- a/Oqtane.Client/Modules/Admin/UserProfile/Index.razor +++ b/Oqtane.Client/Modules/Admin/UserProfile/Index.razor @@ -224,126 +224,133 @@ else

@code { - private string username = string.Empty; - private string _password = string.Empty; - private string _passwordtype = "password"; - private string _togglepassword = string.Empty; - private string confirm = string.Empty; - private bool allowtwofactor = false; - private string twofactor = "False"; - private string email = string.Empty; - private string displayname = string.Empty; - private FileManager filemanager; - private int folderid = -1; - private int photofileid = -1; - private File photo = null; - private List profiles; - private Dictionary settings; - private string category = string.Empty; - private string filter = "to"; - private List notifications; + private string username = string.Empty; + private string _password = string.Empty; + private string _passwordtype = "password"; + private string _togglepassword = string.Empty; + private string confirm = string.Empty; + private bool allowtwofactor = false; + private string twofactor = "False"; + private string email = string.Empty; + private string displayname = string.Empty; + private FileManager filemanager; + private int folderid = -1; + private int photofileid = -1; + private File photo = null; + private List profiles; + private Dictionary settings; + private string category = string.Empty; + private string filter = "to"; + private List notifications; - public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View; - protected override async Task OnParametersSetAsync() - { - try - { - _togglepassword = SharedLocalizer["ShowPassword"]; + protected override async Task OnParametersSetAsync() + { + try + { + _togglepassword = SharedLocalizer["ShowPassword"]; - if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"])) - { - allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true"); - } + if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"])) + { + allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true"); + } - if (PageState.User != null) - { - username = PageState.User.Username; - twofactor = PageState.User.TwoFactorRequired.ToString(); - email = PageState.User.Email; - displayname = PageState.User.DisplayName; + if (PageState.User != null) + { + username = PageState.User.Username; + twofactor = PageState.User.TwoFactorRequired.ToString(); + email = PageState.User.Email; + displayname = PageState.User.DisplayName; - // get user folder - var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath); - if (folder != null) - { - folderid = folder.FolderId; - } + // get user folder + var folder = await FolderService.GetFolderAsync(ModuleState.SiteId, PageState.User.FolderPath); + if (folder != null) + { + folderid = folder.FolderId; + } - if (PageState.User.PhotoFileId != null) - { - photofileid = PageState.User.PhotoFileId.Value; - photo = await FileService.GetFileAsync(photofileid); - } - else - { - photofileid = -1; - photo = null; - } + if (PageState.User.PhotoFileId != null) + { + photofileid = PageState.User.PhotoFileId.Value; + photo = await FileService.GetFileAsync(photofileid); + } + else + { + photofileid = -1; + photo = null; + } - profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); - settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); + profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); + settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); - await LoadNotificationsAsync(); - } - else - { - AddModuleMessage(Localizer["Message.User.NoLogIn"], MessageType.Warning); - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message); - AddModuleMessage(Localizer["Error.Profile.Load"], MessageType.Error); - } - } + await LoadNotificationsAsync(); + } + else + { + AddModuleMessage(Localizer["Message.User.NoLogIn"], MessageType.Warning); + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message); + AddModuleMessage(Localizer["Error.Profile.Load"], MessageType.Error); + } + } - private async Task LoadNotificationsAsync() - { - notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, filter, PageState.User.UserId); - notifications = notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList(); - } + private async Task LoadNotificationsAsync() + { + notifications = await NotificationService.GetNotificationsAsync(PageState.Site.SiteId, filter, PageState.User.UserId); + notifications = notifications.Where(item => item.DeletedBy != PageState.User.Username).ToList(); + } - private string GetProfileValue(string SettingName, string DefaultValue) - => SettingService.GetSetting(settings, SettingName, DefaultValue); + private string GetProfileValue(string SettingName, string DefaultValue) + => SettingService.GetSetting(settings, SettingName, DefaultValue); - private async Task Save() - { - try - { - if (username != string.Empty && email != string.Empty && ValidateProfiles()) - { - if (_password == confirm) - { - var user = PageState.User; - user.Username = username; - user.Password = _password; - user.TwoFactorRequired = bool.Parse(twofactor); - user.Email = email; - user.DisplayName = (displayname == string.Empty ? username : displayname); - user.PhotoFileId = filemanager.GetFileId(); - if (user.PhotoFileId == -1) - { - user.PhotoFileId = null; - } - if (user.PhotoFileId != null) - { - photofileid = user.PhotoFileId.Value; - photo = await FileService.GetFileAsync(photofileid); - } - else - { - photofileid = -1; - photo = null; - } + private async Task Save() + { + try + { + if (username != string.Empty && email != string.Empty && ValidateProfiles()) + { + if (_password == confirm) + { + var user = PageState.User; + user.Username = username; + user.Password = _password; + user.TwoFactorRequired = bool.Parse(twofactor); + user.Email = email; + user.DisplayName = (displayname == string.Empty ? username : displayname); + user.PhotoFileId = filemanager.GetFileId(); + if (user.PhotoFileId == -1) + { + user.PhotoFileId = null; + } + if (user.PhotoFileId != null) + { + photofileid = user.PhotoFileId.Value; + photo = await FileService.GetFileAsync(photofileid); + } + else + { + photofileid = -1; + photo = null; + } - await UserService.UpdateUserAsync(user); - await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); - await logger.LogInformation("User Profile Saved"); + user = await UserService.UpdateUserAsync(user); + if (user != null) + { + await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); + await logger.LogInformation("User Profile Saved"); - AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success); - StateHasChanged(); - } + AddModuleMessage(Localizer["Success.Profile.Update"], MessageType.Success); + StateHasChanged(); + } + else + { + AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error); + } + } else { AddModuleMessage(Localizer["Message.Password.Invalid"], MessageType.Warning); diff --git a/Oqtane.Client/Modules/Admin/Users/Edit.razor b/Oqtane.Client/Modules/Admin/Users/Edit.razor index cb8339c0..6fffed9c 100644 --- a/Oqtane.Client/Modules/Admin/Users/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Users/Edit.razor @@ -201,58 +201,64 @@ else photofileid = -1; photo = null; } - isdeleted = user.IsDeleted.ToString(); + isdeleted = user.IsDeleted.ToString(); lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn); lastipaddress = user.LastIPAddress; settings = await SettingService.GetUserSettingsAsync(user.UserId); - createdby = user.CreatedBy; - createdon = user.CreatedOn; - modifiedby = user.ModifiedBy; - modifiedon = user.ModifiedOn; - deletedby = user.DeletedBy; - deletedon = user.DeletedOn; - } - } - } - catch (Exception ex) - { - await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message); - AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error); - } - } + createdby = user.CreatedBy; + createdon = user.CreatedOn; + modifiedby = user.ModifiedBy; + modifiedon = user.ModifiedOn; + deletedby = user.DeletedBy; + deletedon = user.DeletedOn; + } + } + } + catch (Exception ex) + { + await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message); + AddModuleMessage(Localizer["Error.User.Load"], MessageType.Error); + } + } - private string GetProfileValue(string SettingName, string DefaultValue) - => SettingService.GetSetting(settings, SettingName, DefaultValue); + private string GetProfileValue(string SettingName, string DefaultValue) + => SettingService.GetSetting(settings, SettingName, DefaultValue); - private async Task SaveUser() - { - try - { - if (username != string.Empty && email != string.Empty && ValidateProfiles()) - { - if (_password == confirm) - { - var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); - user.SiteId = PageState.Site.SiteId; - user.Username = username; - user.Password = _password; - user.Email = email; - user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; - user.PhotoFileId = null; - user.PhotoFileId = filemanager.GetFileId(); - if (user.PhotoFileId == -1) - { - user.PhotoFileId = null; - } + private async Task SaveUser() + { + try + { + if (username != string.Empty && email != string.Empty && ValidateProfiles()) + { + if (_password == confirm) + { + var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId); + user.SiteId = PageState.Site.SiteId; + user.Username = username; + user.Password = _password; + user.Email = email; + user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; + user.PhotoFileId = null; + user.PhotoFileId = filemanager.GetFileId(); + if (user.PhotoFileId == -1) + { + user.PhotoFileId = null; + } - user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted)); + user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted)); - user = await UserService.UpdateUserAsync(user); - await SettingService.UpdateUserSettingsAsync(settings, user.UserId); - await logger.LogInformation("User Saved {User}", user); - - NavigationManager.NavigateTo(NavigateUrl()); + user = await UserService.UpdateUserAsync(user); + if (user != null) + { + await SettingService.UpdateUserSettingsAsync(settings, user.UserId); + await logger.LogInformation("User Saved {User}", user); + NavigationManager.NavigateTo(NavigateUrl()); + } + else + { + AddModuleMessage(Localizer["Message.Password.Complexity"], MessageType.Error); + } } else { diff --git a/Oqtane.Client/Resources/Installer/Installer.resx b/Oqtane.Client/Resources/Installer/Installer.resx index fd937839..4d9afff5 100644 --- a/Oqtane.Client/Resources/Installer/Installer.resx +++ b/Oqtane.Client/Resources/Installer/Installer.resx @@ -136,7 +136,7 @@ Please Enter All Required Fields. Ensure Passwords Match And Email Address Provided Is Valid. - The Password Provided Does Not Meet The Password Policy. Please Verify The Minimum Password Length And Complexity Requirements. + The Password Provided Does Not Meet The Complexity Policy. Passwords Must Be At Least 6 Characters In Length And Contain Uppercase, Lowercase, Numeric, And Punctuation Characters. Please Register Me For Major Product Updates And Security Bulletins diff --git a/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx b/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx index 2d09bacc..3a9a097b 100644 --- a/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/UserProfile/Index.resx @@ -120,6 +120,9 @@ Passwords Entered Do Not Match + + Password Provided Does Not Meet The Complexity Policy + From diff --git a/Oqtane.Client/Resources/Modules/Admin/Users/Edit.resx b/Oqtane.Client/Resources/Modules/Admin/Users/Edit.resx index 41446826..b0263e50 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Users/Edit.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Users/Edit.resx @@ -120,6 +120,9 @@ Passwords Entered Do Not Match + + Password Provided Does Not Meet The Complexity Policy + Identity diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 0cfdc502..b872a6a2 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -247,17 +247,33 @@ namespace Oqtane.Controllers if (identityuser != null) { identityuser.Email = user.Email; + var valid = true; if (user.Password != "") { - identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, user.Password); + var validator = new PasswordValidator(); + var result = await validator.ValidateAsync(_identityUserManager, null, user.Password); + valid = result.Succeeded; + if (valid) + { + identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, user.Password); + } + } + if (valid) + { + await _identityUserManager.UpdateAsync(identityuser); + + user = _users.UpdateUser(user); + _syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Update); + _syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Refresh); + user.Password = ""; // remove sensitive information + _logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user); + } + else + { + _logger.Log(user.SiteId, LogLevel.Error, this, LogFunction.Update, "Unable To Update User {Username}. Password Does Not Meet Complexity Requirements.", user.Username); + user = null; } - await _identityUserManager.UpdateAsync(identityuser); } - user = _users.UpdateUser(user); - _syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Update); - _syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Refresh); - user.Password = ""; // remove sensitive information - _logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user); } else {