From 4bdb011b16e012140146ade9a57e5959288f6831 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Date: Mon, 3 May 2021 14:05:17 +0200 Subject: [PATCH] V9: Implementing Error Describers for translated error messages (#10216) * Adding missing keys needed for the translation of the error describers' msgs * Override all methods from IdentityErrorDescriber in order to provide our own translated error messages using our own translation engine (ILocalizedTextService) * Fix tests * Refactoring based on introducing an UmbracoErrorDescriberBase * Remove unnecessary usings --- .../Security/BackOfficeErrorDescriber.cs | 100 ++++++++++++++++- .../Security/UmbracoErrorDescriberBase.cs | 103 ++++++++++++++++++ .../Security/MemberManagerTests.cs | 2 +- .../Security/MemberSignInManagerTests.cs | 2 +- .../umbraco/config/lang/en_us.xml | 30 ++++- 5 files changed, 229 insertions(+), 8 deletions(-) create mode 100644 src/Umbraco.Infrastructure/Security/UmbracoErrorDescriberBase.cs diff --git a/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs b/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs index 60d913f7c5..c0021bc967 100644 --- a/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs +++ b/src/Umbraco.Infrastructure/Security/BackOfficeErrorDescriber.cs @@ -1,17 +1,109 @@ using Microsoft.AspNetCore.Identity; +using Umbraco.Cms.Core.Services; +using Umbraco.Extensions; namespace Umbraco.Cms.Core.Security { /// /// Umbraco back office specific /// - public class BackOfficeErrorDescriber : IdentityErrorDescriber + public class BackOfficeErrorDescriber : UmbracoErrorDescriberBase { - // TODO: Override all the methods in order to provide our own translated error messages + private readonly ILocalizedTextService _textService; + + public BackOfficeErrorDescriber(ILocalizedTextService textService) + : base(textService) => _textService = textService; + + public override IdentityError DuplicateRoleName(string role) => new IdentityError + { + Code = nameof(DuplicateRoleName), + Description = _textService.Localize("validation/duplicateUserGroupName", new[] { role }) + }; + + public override IdentityError InvalidRoleName(string role) => new IdentityError + { + Code = nameof(InvalidRoleName), + Description = _textService.Localize("validation/invalidUserGroupName") + }; + + public override IdentityError LoginAlreadyAssociated() => new IdentityError + { + Code = nameof(LoginAlreadyAssociated), + Description = _textService.Localize("user/duplicateLogin") + }; + + public override IdentityError UserAlreadyHasPassword() => new IdentityError + { + Code = nameof(UserAlreadyHasPassword), + Description = _textService.Localize("user/userHasPassword") + }; + + public override IdentityError UserAlreadyInRole(string role) => new IdentityError + { + Code = nameof(UserAlreadyInRole), + Description = _textService.Localize("user/userHasGroup", new[] { role }) + }; + + public override IdentityError UserLockoutNotEnabled() => new IdentityError + { + Code = nameof(UserLockoutNotEnabled), + Description = _textService.Localize("user/userLockoutNotEnabled") + }; + + public override IdentityError UserNotInRole(string role) => new IdentityError + { + Code = nameof(UserNotInRole), + Description = _textService.Localize("user/userNotInGroup", new[] { role }) + }; } - public class MembersErrorDescriber : IdentityErrorDescriber + public class MembersErrorDescriber : UmbracoErrorDescriberBase { - // TODO: Override all the methods in order to provide our own translated error messages + private readonly ILocalizedTextService _textService; + + public MembersErrorDescriber(ILocalizedTextService textService) + : base(textService) => _textService = textService; + + public override IdentityError DuplicateRoleName(string role) => new IdentityError + { + Code = nameof(DuplicateRoleName), + Description = _textService.Localize("validation/duplicateMemberGroupName", new[] { role }) + }; + + public override IdentityError InvalidRoleName(string role) => new IdentityError + { + Code = nameof(InvalidRoleName), + Description = _textService.Localize("validation/invalidMemberGroupName") + }; + + public override IdentityError LoginAlreadyAssociated() => new IdentityError + { + Code = nameof(LoginAlreadyAssociated), + Description = _textService.Localize("member/duplicateMemberLogin") + }; + + public override IdentityError UserAlreadyHasPassword() => new IdentityError + { + Code = nameof(UserAlreadyHasPassword), + Description = _textService.Localize("member/memberHasPassword") + }; + + public override IdentityError UserAlreadyInRole(string role) => new IdentityError + { + Code = nameof(UserAlreadyInRole), + Description = _textService.Localize("member/memberHasGroup", new[] { role }) + }; + + public override IdentityError UserLockoutNotEnabled() => new IdentityError + { + Code = nameof(UserLockoutNotEnabled), + Description = _textService.Localize("member/memberLockoutNotEnabled") + }; + + public override IdentityError UserNotInRole(string role) => new IdentityError + { + Code = nameof(UserNotInRole), + Description = _textService.Localize("member/memberNotInGroup", new[] { role }) + }; } } diff --git a/src/Umbraco.Infrastructure/Security/UmbracoErrorDescriberBase.cs b/src/Umbraco.Infrastructure/Security/UmbracoErrorDescriberBase.cs new file mode 100644 index 0000000000..0214811274 --- /dev/null +++ b/src/Umbraco.Infrastructure/Security/UmbracoErrorDescriberBase.cs @@ -0,0 +1,103 @@ +using Microsoft.AspNetCore.Identity; +using Umbraco.Cms.Core.Services; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Security +{ + public abstract class UmbracoErrorDescriberBase : IdentityErrorDescriber + { + private readonly ILocalizedTextService _textService; + + protected UmbracoErrorDescriberBase(ILocalizedTextService textService) => _textService = textService; + + public override IdentityError ConcurrencyFailure() => new IdentityError + { + Code = nameof(ConcurrencyFailure), + Description = _textService.Localize("errors/concurrencyError") + }; + + public override IdentityError DefaultError() => new IdentityError + { + Code = nameof(DefaultError), + Description = _textService.Localize("errors/defaultError") + }; + + public override IdentityError DuplicateEmail(string email) => new IdentityError + { + Code = nameof(DuplicateEmail), + Description = _textService.Localize("validation/duplicateEmail", new[] { email }) + }; + + public override IdentityError DuplicateUserName(string userName) => new IdentityError + { + Code = nameof(DuplicateUserName), + Description = _textService.Localize("validation/duplicateUsername", new[] { userName }) + }; + + public override IdentityError InvalidEmail(string email) => new IdentityError + { + Code = nameof(InvalidEmail), + Description = _textService.Localize("validation/invalidEmail") + }; + + public override IdentityError InvalidToken() => new IdentityError + { + Code = nameof(InvalidToken), + Description = _textService.Localize("validation/invalidToken") + }; + + public override IdentityError InvalidUserName(string userName) => new IdentityError + { + Code = nameof(InvalidUserName), + Description = _textService.Localize("validation/invalidUsername") + }; + + public override IdentityError PasswordMismatch() => new IdentityError + { + Code = nameof(PasswordMismatch), + Description = _textService.Localize("user/passwordMismatch") + }; + + public override IdentityError PasswordRequiresDigit() => new IdentityError + { + Code = nameof(PasswordRequiresDigit), + Description = _textService.Localize("user/passwordRequiresDigit") + }; + + public override IdentityError PasswordRequiresLower() => new IdentityError + { + Code = nameof(PasswordRequiresLower), + Description = _textService.Localize("user/passwordRequiresLower") + }; + + public override IdentityError PasswordRequiresNonAlphanumeric() => new IdentityError + { + Code = nameof(PasswordRequiresNonAlphanumeric), + Description = _textService.Localize("user/passwordRequiresNonAlphanumeric") + }; + + public override IdentityError PasswordRequiresUniqueChars(int uniqueChars) => new IdentityError + { + Code = nameof(PasswordRequiresUniqueChars), + Description = _textService.Localize("user/passwordRequiresUniqueChars", new[] { uniqueChars.ToString() }) + }; + + public override IdentityError PasswordRequiresUpper() => new IdentityError + { + Code = nameof(PasswordRequiresUpper), + Description = _textService.Localize("user/passwordRequiresUpper") + }; + + public override IdentityError PasswordTooShort(int length) => new IdentityError + { + Code = nameof(PasswordTooShort), + Description = _textService.Localize("user/passwordTooShort", new[] { length.ToString() }) + }; + + public override IdentityError RecoveryCodeRedemptionFailed() => new IdentityError + { + Code = nameof(RecoveryCodeRedemptionFailed), + Description = _textService.Localize("login/resetCodeExpired") + }; + } +} diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs index f773b83e5a..a4aaa9311c 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs @@ -73,7 +73,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security _mockPasswordHasher.Object, userValidators, pwdValidators, - new MembersErrorDescriber(), + new MembersErrorDescriber(Mock.Of()), _mockServiceProviders.Object, new Mock>>().Object, _mockPasswordConfiguration.Object, diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Security/MemberSignInManagerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Security/MemberSignInManagerTests.cs index 747148b3ee..a49ba1a8ac 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Security/MemberSignInManagerTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Security/MemberSignInManagerTests.cs @@ -65,7 +65,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Security Mock.Of>(), Enumerable.Empty>(), Enumerable.Empty>(), - new MembersErrorDescriber(), + new MembersErrorDescriber(Mock.Of()), Mock.Of(), Mock.Of>>(), Options.Create(new MemberPasswordConfigurationSettings()), diff --git a/src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml index 3885f5e338..61c14f854e 100644 --- a/src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI.NetCore/umbraco/config/lang/en_us.xml @@ -358,9 +358,14 @@ Upload is not allowed in this location. - Create a new member All Members + Create a new member + A member with this login already exists Member groups have no additional properties for editing. + The member is already in group '%0%' + The member already has a password set + Lockout is not enabled for this member + The member is not in group '%0%' Failed to copy content type @@ -656,6 +661,8 @@ %0% is not in a correct format + An unknown failure has occurred + Optimistic concurrency failure, object has been modified Received an error from the server The specified file type has been disallowed by the administrator NOTE! Even though CodeMirror is enabled by configuration, it is disabled in Internet Explorer because it's not stable enough. @@ -1444,7 +1451,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Validation errors must be fixed before the item can be saved Failed Saved - Saved. To view the changes please reload your browser + Saved. To view the changes please reload your browser Insufficient user permissions, could not complete the operation Cancelled Operation was cancelled by a 3rd party add-in @@ -1908,6 +1915,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Description field Disable User Document Type + A user with this login already exists Editor Excerpt field Failed login attempts @@ -1940,6 +1948,12 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Invalid current password There was a difference between the new password and the confirmed password. Please try again! The confirmed password doesn't match the new password! + The password must have at least one digit ('0'-'9') + The password must have at least one lowercase ('a'-'z') + The password must have at least one non alphanumeric character + The password must use at least %0% different characters + The password must have at least one uppercase ('A'-'Z') + The password must be at least %0% characters long Replace child node permissions You are currently modifying permissions for the pages: Select pages to modify their permissions @@ -1960,8 +1974,12 @@ To manage your website, simply open the Umbraco backoffice and start adding cont User last updated has been created The new user has successfully been created. To log in to Umbraco use the password below. + The user already has a password set + The user is already in group '%0%' + Lockout is not enabled for this user User management Name + The user is not in group '%0%' User permissions User group has been invited @@ -2111,6 +2129,14 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Value cannot be null Value cannot be empty Value is invalid, it does not match the correct pattern + Invalid member group name + Invalid user group name + Invalid token + Invalid username + Email '%0%' is already taken + User group name '%0%' is already taken + Member group name '%0%' is already taken + Username '%0%' is already taken Custom validation %1% more.]]> %1% too many.]]>