mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-01-22 21:02:08 +08:00
Added Username policies (#8638)
* Added Username policies * Added newline at the end of files # Conflicts: # src/Orchard.Web/Modules/Orchard.Users/Services/AccountValidationService.cs * Added check for username length that must be under 255 characters (even if username policies are disabled). If username isn't modified, policies are not enforced when saving the user inside backoffice. Default length limits are 1 and 255. * Added UsernameValidationError.cs Added a setting to bypass non fatal errors and show them as warning when creating/editing users from the backoffice Added the relative checkbox in RegistrationSettings.cshtml Modified the UsernameMeetsPolicies method to use the new class Modified AdminController (CreatePOST, EditPOST) and AccountController (Register) * If username is an email check that it matches the specified email * Added hints to UserRegistrationSettings view Changed the severity of some custom policies errors * Removed UsernameValidLengthAttribute.cs, if MinimumUsernameLength and MaximumUsernameLength settings don't make sense these settings are ignored * bugfix. The admin could change the a username setting an already existing username. Co-authored-by: Andrea Piovanelli <andrea.piovanelli@laser-group.com>
This commit is contained in:
committed by
GitHub
parent
028e2e413b
commit
c515ce1917
@@ -0,0 +1,10 @@
|
||||
namespace Orchard.Users.Constants {
|
||||
public static class UsernameValidationResults {
|
||||
public const string UsernameIsTooShort = "UsernameIsTooShort";
|
||||
public const string UsernameIsTooLong = "UsernameIsTooLong";
|
||||
public const string UsernameContainsWhitespaces = "UsernameContainsWhitespaces";
|
||||
public const string UsernameContainsSpecialChars = "UsernameContainsSpecialChars";
|
||||
public const string UsernameAndEmailMustMatch = "UsernameAndEmailMustMatch";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ using Orchard.Users.Models;
|
||||
using Orchard.Users.Services;
|
||||
using Orchard.Users.ViewModels;
|
||||
using Orchard.Utility.Extensions;
|
||||
using Orchard.Mvc.Html;
|
||||
using Orchard.Users.Constants;
|
||||
|
||||
namespace Orchard.Users.Controllers {
|
||||
[ValidateInput(false)]
|
||||
@@ -28,6 +30,7 @@ namespace Orchard.Users.Controllers {
|
||||
private readonly IUserEventHandler _userEventHandlers;
|
||||
private readonly ISiteService _siteService;
|
||||
private readonly IEnumerable<IUserManagementActionsProvider> _userManagementActionsProviders;
|
||||
private readonly UrlHelper _urlHelper;
|
||||
|
||||
public AdminController(
|
||||
IOrchardServices services,
|
||||
@@ -36,7 +39,8 @@ namespace Orchard.Users.Controllers {
|
||||
IShapeFactory shapeFactory,
|
||||
IUserEventHandler userEventHandlers,
|
||||
ISiteService siteService,
|
||||
IEnumerable<IUserManagementActionsProvider> userManagementActionsProviders) {
|
||||
IEnumerable<IUserManagementActionsProvider> userManagementActionsProviders,
|
||||
UrlHelper urlHelper) {
|
||||
|
||||
Services = services;
|
||||
_membershipService = membershipService;
|
||||
@@ -44,6 +48,7 @@ namespace Orchard.Users.Controllers {
|
||||
_userEventHandlers = userEventHandlers;
|
||||
_siteService = siteService;
|
||||
_userManagementActionsProviders = userManagementActionsProviders;
|
||||
_urlHelper = urlHelper;
|
||||
|
||||
T = NullLocalizer.Instance;
|
||||
Shape = shapeFactory;
|
||||
@@ -183,11 +188,34 @@ namespace Orchard.Users.Controllers {
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage users")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
IDictionary<string, LocalizedString> validationErrors;
|
||||
List<UsernameValidationError> usernameValidationErrors = new List<UsernameValidationError>();
|
||||
|
||||
|
||||
bool usernameMeetsPolicies = true;
|
||||
var settings = _siteService.GetSiteSettings().As<RegistrationSettingsPart>();
|
||||
|
||||
if (!string.IsNullOrEmpty(createModel.UserName)) {
|
||||
usernameMeetsPolicies = _userService.UsernameMeetsPolicies(createModel.UserName, createModel.Email, out usernameValidationErrors);
|
||||
if (!usernameMeetsPolicies) {
|
||||
// If this setting is enabled we'd like to show the warning message but we can't right now
|
||||
// because we didn't create the user yet (and maybe we won't if some other validation fails)
|
||||
// and we can't generate the link to the edit page properly so here we only handle the
|
||||
// situation where we have to show warnings as errors.
|
||||
if (!settings.BypassPoliciesFromBackoffice) {
|
||||
ShowWarningAsErrors(usernameValidationErrors);
|
||||
}
|
||||
// Show fatal errors anyway
|
||||
ShowFatalErrors(usernameValidationErrors);
|
||||
}
|
||||
if (!_userService.VerifyUserUnicity(createModel.UserName, createModel.Email)) {
|
||||
AddModelError("NotUniqueUserName", T("User with that username and/or email already exists."));
|
||||
}
|
||||
}
|
||||
else {
|
||||
AddModelError(UsernameValidationResults.UsernameIsTooShort, T("The username must not be empty."));
|
||||
}
|
||||
|
||||
|
||||
if (!Regex.IsMatch(createModel.Email ?? "", UserPart.EmailPattern, RegexOptions.IgnoreCase)) {
|
||||
// http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx
|
||||
@@ -198,12 +226,12 @@ namespace Orchard.Users.Controllers {
|
||||
AddModelError("ConfirmPassword", T("Password confirmation must match"));
|
||||
}
|
||||
|
||||
IDictionary<string, LocalizedString> validationErrors;
|
||||
|
||||
if (!_userService.PasswordMeetsPolicies(createModel.Password, null, out validationErrors)) {
|
||||
ModelState.AddModelErrors(validationErrors);
|
||||
}
|
||||
|
||||
|
||||
var user = Services.ContentManager.New<IUser>("User");
|
||||
if (ModelState.IsValid) {
|
||||
user = _membershipService.CreateUser(new CreateUserParams(
|
||||
@@ -214,6 +242,12 @@ namespace Orchard.Users.Controllers {
|
||||
createModel.ForcePasswordChange));
|
||||
}
|
||||
|
||||
// Now that the user has been created we check if we have to show the warning since now we can generate the link
|
||||
// to the user edit page
|
||||
if (!usernameMeetsPolicies && settings.BypassPoliciesFromBackoffice && usernameValidationErrors.Any(uve => uve.Severity == Severity.Warning)) {
|
||||
Services.Notifier.Warning(T("The username <a href=\"{0}\">{1}</a> doesn't meet the custom requirements.", _urlHelper.ItemEditUrl(user), createModel.UserName));
|
||||
}
|
||||
|
||||
var model = Services.ContentManager.UpdateEditor(user, this);
|
||||
|
||||
if (!ModelState.IsValid) {
|
||||
@@ -253,6 +287,22 @@ namespace Orchard.Users.Controllers {
|
||||
return View(model);
|
||||
}
|
||||
|
||||
private void ShowWarningAsErrors(List<UsernameValidationError> validationErrors) {
|
||||
if (validationErrors.Any(uve => uve.Severity == Severity.Warning)) {
|
||||
foreach (var uve in validationErrors.Where(uve => uve.Severity == Severity.Warning)) {
|
||||
AddModelError(uve.Key, uve.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowFatalErrors(List<UsernameValidationError> validationErrors) {
|
||||
if (validationErrors.Any(uve => uve.Severity == Severity.Fatal)) {
|
||||
foreach (var uve in validationErrors.Where(uve => uve.Severity == Severity.Fatal)) {
|
||||
AddModelError(uve.Key, uve.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Edit")]
|
||||
public ActionResult EditPOST(int id) {
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageUsers, T("Not authorized to manage users")))
|
||||
@@ -274,10 +324,34 @@ namespace Orchard.Users.Controllers {
|
||||
|
||||
var editModel = new UserEditViewModel { User = user };
|
||||
if (TryUpdateModel(editModel)) {
|
||||
if (!_userService.VerifyUserUnicity(id, editModel.UserName, editModel.Email)) {
|
||||
AddModelError("NotUniqueUserName", T("User with that username and/or email already exists."));
|
||||
List<UsernameValidationError> validationErrors;
|
||||
bool usernameMeetsPolicies = _userService.UsernameMeetsPolicies(editModel.UserName, editModel.Email, out validationErrors);
|
||||
var settings = _siteService.GetSiteSettings().As<RegistrationSettingsPart>();
|
||||
// Username has been modified
|
||||
if (!previousName.Equals(editModel.UserName)) {
|
||||
if (!usernameMeetsPolicies && settings.BypassPoliciesFromBackoffice) {
|
||||
// If warnings have to be bypassed and there's at least one warning we show a generic warning message
|
||||
if (validationErrors.Any(uve => uve.Severity == Severity.Warning)) {
|
||||
Services.Notifier.Warning(T("The username <a href=\"{0}\">{1}</a> doesn't meet the custom requirements.", _urlHelper.ItemEditUrl(user), editModel.UserName));
|
||||
}
|
||||
}
|
||||
else if (!usernameMeetsPolicies) {
|
||||
// If warnings don't have to be bypassed we show everyone of them as errors
|
||||
ShowWarningAsErrors(validationErrors);
|
||||
}
|
||||
}
|
||||
else if (!Regex.IsMatch(editModel.Email ?? "", UserPart.EmailPattern, RegexOptions.IgnoreCase)) {
|
||||
else {
|
||||
if (!usernameMeetsPolicies && settings.BypassPoliciesFromBackoffice) {
|
||||
// If warnings have to be bypassed and there's at least one warning we show a generic warning message
|
||||
if (validationErrors.Any(uve => uve.Severity == Severity.Warning)) {
|
||||
Services.Notifier.Warning(T("The username <a href=\"{0}\">{1}</a> doesn't meet the custom requirements.", _urlHelper.ItemEditUrl(user), editModel.UserName));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Show every Fatal validation error
|
||||
ShowFatalErrors(validationErrors);
|
||||
|
||||
if (!Regex.IsMatch(editModel.Email ?? "", UserPart.EmailPattern, RegexOptions.IgnoreCase)) {
|
||||
// http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx
|
||||
ModelState.AddModelError("Email", T("You must specify a valid email address."));
|
||||
}
|
||||
@@ -289,6 +363,10 @@ namespace Orchard.Users.Controllers {
|
||||
|
||||
user.NormalizedUserName = editModel.UserName.ToLowerInvariant();
|
||||
}
|
||||
|
||||
if (!_userService.VerifyUserUnicity(id, editModel.UserName, editModel.Email)) {
|
||||
AddModelError("NotUniqueUserName", T("User with that username and/or email already exists."));
|
||||
}
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid) {
|
||||
@@ -422,3 +500,4 @@ namespace Orchard.Users.Controllers {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Orchard.Security {
|
||||
using Orchard.Users.Models;
|
||||
|
||||
namespace Orchard.Security {
|
||||
public static class MembershipSettingsExtensions {
|
||||
public static int GetMinimumPasswordLength(this IMembershipSettings membershipSettings) {
|
||||
return membershipSettings.EnableCustomPasswordPolicy ? membershipSettings.MinimumPasswordLength : 7;
|
||||
@@ -21,5 +23,13 @@
|
||||
public static int GetPasswordReuseLimit(this IMembershipSettings membershipSettings) {
|
||||
return membershipSettings.EnablePasswordHistoryPolicy ? membershipSettings.PasswordReuseLimit : 5;
|
||||
}
|
||||
|
||||
public static int GetMinimumUsernameLength(this IMembershipSettings membershipSettings) {
|
||||
return membershipSettings.EnableCustomUsernamePolicy ? membershipSettings.MinimumUsernameLength : 3;
|
||||
}
|
||||
|
||||
public static int GetMaximumUsernameLength(this IMembershipSettings membershipSettings) {
|
||||
return membershipSettings.EnableCustomUsernamePolicy ? membershipSettings.MaximumUsernameLength : UserPart.MaxUserNameLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,5 +101,46 @@ namespace Orchard.Users.Models {
|
||||
get { return this.Retrieve(x => x.PasswordReuseLimit, 5); }
|
||||
set { this.Store(x => x.PasswordReuseLimit, value); }
|
||||
}
|
||||
|
||||
public bool EnableCustomUsernamePolicy {
|
||||
get { return this.Retrieve(x => x.EnableCustomUsernamePolicy); }
|
||||
set { this.Store(x => x.EnableCustomUsernamePolicy, value); }
|
||||
}
|
||||
|
||||
[Range(1, UserPart.MaxUserNameLength, ErrorMessage = "The minimum username length must be between 1 and 255.")]
|
||||
public int MinimumUsernameLength {
|
||||
get { return this.Retrieve(x => x.MinimumUsernameLength, 1); }
|
||||
set { this.Store(x => x.MinimumUsernameLength, value); }
|
||||
|
||||
}
|
||||
|
||||
[Range(1, UserPart.MaxUserNameLength, ErrorMessage = "The maximum username length must be between 1 and 255.")]
|
||||
public int MaximumUsernameLength {
|
||||
get { return this.Retrieve(x => x.MaximumUsernameLength, UserPart.MaxUserNameLength); }
|
||||
set { this.Store(x => x.MaximumUsernameLength, value); }
|
||||
}
|
||||
|
||||
public bool ForbidUsernameSpecialChars {
|
||||
get { return this.Retrieve(x => x.ForbidUsernameSpecialChars); }
|
||||
set { this.Store(x => x.ForbidUsernameSpecialChars, value); }
|
||||
}
|
||||
|
||||
public bool AllowEmailAsUsername {
|
||||
get { return this.Retrieve(x => x.AllowEmailAsUsername); }
|
||||
set { this.Store(x => x.AllowEmailAsUsername, value); }
|
||||
}
|
||||
|
||||
public bool ForbidUsernameWhitespace {
|
||||
get { return this.Retrieve(x => x.ForbidUsernameWhitespace); }
|
||||
set { this.Store(x => x.ForbidUsernameWhitespace, value); }
|
||||
}
|
||||
|
||||
public bool BypassPoliciesFromBackoffice {
|
||||
get { return this.Retrieve(x => x.BypassPoliciesFromBackoffice); }
|
||||
set { this.Store(x => x.BypassPoliciesFromBackoffice, value); }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
<Compile Include="Activities\UserActivity.cs" />
|
||||
<Compile Include="Activities\UserIsApprovedActivity.cs" />
|
||||
<Compile Include="Commands\UserCommands.cs" />
|
||||
<Compile Include="Constants\UsernameValidationResults.cs" />
|
||||
<Compile Include="Constants\UserPasswordValidationResults.cs" />
|
||||
<Compile Include="Controllers\AccountController.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
@@ -155,6 +156,7 @@
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Services\MissingSettingsBanner.cs" />
|
||||
<Compile Include="Services\UserService.cs" />
|
||||
<Compile Include="Services\UsernameValidationError.cs" />
|
||||
<Compile Include="ViewModels\UserCreateViewModel.cs" />
|
||||
<Compile Include="ViewModels\UserEditPasswordViewModel.cs" />
|
||||
<Compile Include="ViewModels\UserEditViewModel.cs" />
|
||||
|
||||
@@ -35,13 +35,16 @@ namespace Orchard.Users.Services {
|
||||
|
||||
return context.ValidationSuccessful;
|
||||
}
|
||||
|
||||
public bool ValidateUserName(AccountValidationContext context) {
|
||||
List <UsernameValidationError> validationErrors;
|
||||
_userService.UsernameMeetsPolicies(context.UserName, context.Email, out validationErrors);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(context.UserName)) {
|
||||
context.ValidationErrors.Add("username", T("You must specify a username."));
|
||||
} else if (context.UserName.Length >= UserPart.MaxUserNameLength) {
|
||||
context.ValidationErrors.Add("username", T("The username you provided is too long."));
|
||||
if (validationErrors != null && validationErrors.Any()) {
|
||||
foreach (var err in validationErrors) {
|
||||
if (!context.ValidationErrors.ContainsKey(err.Key)) {
|
||||
context.ValidationErrors.Add(err.Key, err.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return context.ValidationSuccessful;
|
||||
@@ -63,3 +66,4 @@ namespace Orchard.Users.Services {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,5 +20,6 @@ namespace Orchard.Users.Services {
|
||||
bool DecryptNonce(string challengeToken, out string username, out DateTime validateByUtc);
|
||||
|
||||
bool PasswordMeetsPolicies(string password, IUser user, out IDictionary<string, LocalizedString> validationErrors);
|
||||
bool UsernameMeetsPolicies(string username, string email, out List<UsernameValidationError> validationErrors);
|
||||
}
|
||||
}
|
||||
@@ -245,6 +245,66 @@ namespace Orchard.Users.Services {
|
||||
return validationErrors.Count == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool UsernameMeetsPolicies(string username, string email, out List<UsernameValidationError> validationErrors) {
|
||||
validationErrors = new List<UsernameValidationError>();
|
||||
var settings = _siteService.GetSiteSettings().As<RegistrationSettingsPart>();
|
||||
|
||||
if (string.IsNullOrEmpty(username)) {
|
||||
validationErrors.Add(new UsernameValidationError(Severity.Fatal, UsernameValidationResults.UsernameIsTooShort,
|
||||
T("The username must not be empty.")));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate username length to check it's not over 255.
|
||||
if (username.Length > UserPart.MaxUserNameLength) {
|
||||
validationErrors.Add(new UsernameValidationError(Severity.Fatal, UsernameValidationResults.UsernameIsTooLong,
|
||||
T("The username can't be longer than {0} characters.", UserPart.MaxUserNameLength)));
|
||||
return false;
|
||||
}
|
||||
|
||||
var usernameIsEmail = Regex.IsMatch(username, UserPart.EmailPattern, RegexOptions.IgnoreCase);
|
||||
|
||||
if (usernameIsEmail && !username.Equals(email, StringComparison.OrdinalIgnoreCase)){
|
||||
validationErrors.Add(new UsernameValidationError(Severity.Fatal, UsernameValidationResults.UsernameAndEmailMustMatch,
|
||||
T("If the username is an email it must match the specified email address.")));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.EnableCustomUsernamePolicy) {
|
||||
|
||||
/// If the Maximum username length is smaller than the Minimum username length settings ignore this setting
|
||||
if (settings.GetMaximumUsernameLength() >= settings.GetMinimumUsernameLength() && username.Length < settings.GetMinimumUsernameLength()) {
|
||||
if (!settings.AllowEmailAsUsername || !usernameIsEmail) {
|
||||
validationErrors.Add(new UsernameValidationError(Severity.Warning, UsernameValidationResults.UsernameIsTooShort,
|
||||
T("You must specify a username of {0} or more characters.", settings.GetMinimumUsernameLength())));
|
||||
}
|
||||
}
|
||||
|
||||
/// If the Minimum username length is greater than the Maximum username length settings ignore this setting
|
||||
if (settings.GetMaximumUsernameLength() >= settings.GetMinimumUsernameLength() && username.Length > settings.GetMaximumUsernameLength()) {
|
||||
if (!settings.AllowEmailAsUsername || !usernameIsEmail) {
|
||||
validationErrors.Add(new UsernameValidationError(Severity.Warning, UsernameValidationResults.UsernameIsTooLong,
|
||||
T("You must specify a username of at most {0} characters.", settings.GetMaximumUsernameLength())));
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.ForbidUsernameWhitespace && username.Any(x => char.IsWhiteSpace(x))) {
|
||||
validationErrors.Add(new UsernameValidationError(Severity.Warning, UsernameValidationResults.UsernameContainsWhitespaces,
|
||||
T("The username must not contain whitespaces.")));
|
||||
}
|
||||
|
||||
if (settings.ForbidUsernameSpecialChars && Regex.Match(username, "[^a-zA-Z0-9]").Success) {
|
||||
if (!settings.AllowEmailAsUsername || !usernameIsEmail) {
|
||||
validationErrors.Add(new UsernameValidationError(Severity.Warning, UsernameValidationResults.UsernameContainsSpecialChars,
|
||||
T("The username must not contain special characters.")));
|
||||
}
|
||||
}
|
||||
}
|
||||
return validationErrors.Count == 0;
|
||||
}
|
||||
|
||||
public UserPart GetUserByNameOrEmail(string usernameOrEmail) {
|
||||
var lowerName = usernameOrEmail.ToLowerInvariant();
|
||||
return _contentManager
|
||||
@@ -255,3 +315,4 @@ namespace Orchard.Users.Services {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Users.Services {
|
||||
|
||||
public enum Severity {
|
||||
Warning,
|
||||
Fatal
|
||||
}
|
||||
|
||||
public class UsernameValidationError {
|
||||
|
||||
private Severity _severity;
|
||||
private string _key;
|
||||
private LocalizedString _errorMessage;
|
||||
|
||||
public UsernameValidationError(Severity severity, string key, LocalizedString errorMessage) {
|
||||
Severity = severity;
|
||||
Key = key;
|
||||
ErrorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public Severity Severity { get => _severity; set => _severity = value; }
|
||||
public string Key { get => _key; set => _key = value; }
|
||||
public LocalizedString ErrorMessage { get => _errorMessage; set => _errorMessage = value; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
@model Orchard.Users.Models.RegistrationSettingsPart
|
||||
@using Orchard.Messaging.Services;
|
||||
@using System.Web.Security;
|
||||
@using Orchard.Users.Models;
|
||||
|
||||
@{
|
||||
var messageManager = WorkContext.Resolve<IMessageManager>();
|
||||
@@ -13,6 +14,45 @@
|
||||
@Html.EditorFor(m => m.UsersCanRegister)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.UsersCanRegister)">@T("Users can create new accounts on the site")</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@Html.EditorFor(m => m.EnableCustomUsernamePolicy)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.EnableCustomUsernamePolicy)">@T("Username must meet custom requirements")</label>
|
||||
<div data-controllerid="@Html.FieldIdFor(m => m.EnableCustomUsernamePolicy)" style="margin-left: 30px;">
|
||||
<div>
|
||||
@Html.EditorFor(m => m.BypassPoliciesFromBackoffice)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.BypassPoliciesFromBackoffice)">@T("Allow backend users to bypass the custom policies and show a warning when that happens.")</label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="@Html.FieldIdFor(m => m.MinimumUsernameLength)">@T("Minimum Username length")</label>
|
||||
@Html.TextBoxFor(m => m.MinimumUsernameLength, new { @class = "text medium", @Value = Model.MinimumUsernameLength })
|
||||
<span class="hint">@T("Minimum value allowed is 1")</span>
|
||||
<span class="hint">@T("If this value is greater than the value set for Maximum Username length this setting will be ignored.")</span>
|
||||
@Html.ValidationMessage("MinimumUsernameLength", "*")
|
||||
</div>
|
||||
<div>
|
||||
<label for="@Html.FieldIdFor(m => m.MaximumUsernameLength)">@T("Maximum Username length")</label>
|
||||
@Html.TextBoxFor(m => m.MaximumUsernameLength, new { @class = "text medium", @Value = Model.MaximumUsernameLength })
|
||||
<span class="hint">@T("Maximum value allowed is {0}", UserPart.MaxUserNameLength)</span>
|
||||
<span class="hint">@T("If this value is smaller than the value set for Minimum Username length this setting will be ignored.")</span>
|
||||
@Html.ValidationMessage("MaximumUsernameLength", "*")
|
||||
</div>
|
||||
<div>
|
||||
@Html.EditorFor(m => m.ForbidUsernameWhitespace)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.ForbidUsernameWhitespace)">@T("Username must not contain whitespaces")</label>
|
||||
</div>
|
||||
<div>
|
||||
@Html.EditorFor(m => m.ForbidUsernameSpecialChars)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.ForbidUsernameSpecialChars)">@T("Username must not contain special characters")</label>
|
||||
<div data-controllerid="@Html.FieldIdFor(m => m.ForbidUsernameSpecialChars)" style="margin-left: 30px;">
|
||||
@Html.EditorFor(m => m.AllowEmailAsUsername)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.AllowEmailAsUsername)">@T("Username may be an email")</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
@Html.EditorFor(m => m.EnableCustomPasswordPolicy)
|
||||
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.EnableCustomPasswordPolicy)">@T("Passwords must meet custom requirements")</label>
|
||||
@@ -126,4 +166,5 @@
|
||||
@Html.ValidationMessage("NotificationsRecipients", "*")
|
||||
<span class="hint">@T("The usernames to send the notifications to (e.g., \"admin, user1, ...\").")</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -21,5 +21,13 @@ namespace Orchard.Security {
|
||||
MembershipPasswordFormat PasswordFormat { get; set; }
|
||||
bool EnablePasswordHistoryPolicy { get; set; }
|
||||
int PasswordReuseLimit { get; set; }
|
||||
bool EnableCustomUsernamePolicy { get; set; }
|
||||
int MinimumUsernameLength { get; set; }
|
||||
int MaximumUsernameLength { get; set; }
|
||||
bool ForbidUsernameSpecialChars { get; set; }
|
||||
bool AllowEmailAsUsername {get; set;}
|
||||
bool ForbidUsernameWhitespace { get; set; }
|
||||
bool BypassPoliciesFromBackoffice { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user