Merge pull request #5156 from abpframework/maliming/two-factor

Add Identity Two-factor features and settings.
pull/5409/head
Halil İbrahim Kalkan 5 years ago committed by GitHub
commit 624520a0c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,13 +1,23 @@
@page
@page
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Options
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@using Volo.Abp.FeatureManagement.Localization
@using Volo.Abp.Validation.StringValues
@using Volo.Abp.FeatureManagement.Web.Pages.FeatureManagement
@using Volo.Abp.Localization
@model FeatureManagementModal
@inject IHtmlLocalizer<AbpFeatureManagementResource> L
@inject IHtmlLocalizerFactory HtmlLocalizerFactory
@inject IOptions<AbpLocalizationOptions> LocalizationOptions
@{
Layout = null;
IHtmlLocalizer CreateHtmlLocalizer(string resourceName)
{
var resource = LocalizationOptions.Value.Resources.Values.FirstOrDefault(x => x.ResourceName == resourceName);
return HtmlLocalizerFactory.Create(resource != null ? resource.ResourceType : LocalizationOptions.Value.DefaultResourceType);
}
}
<form method="post" asp-page="/FeatureManagement/FeatureManagementModal" data-script-class="abp.modals.FeatureManagement">
<abp-modal size="Large">
@ -48,11 +58,11 @@
{
if (item.Value == feature.Value)
{
<option value="@item.Value" selected="selected"> @L.GetString(item.DisplayText.Name) </option>
<option value="@item.Value" selected="selected"> @CreateHtmlLocalizer(item.DisplayText.ResourceName).GetString(item.DisplayText.Name) </option>
}
else
{
<option value="@item.Value"> @L.GetString(item.DisplayText.Name) </option>
<option value="@item.Value"> @CreateHtmlLocalizer(item.DisplayText.ResourceName).GetString(item.DisplayText.Name) </option>
}
}
</select>

@ -23,6 +23,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\users\src\Volo.Abp.Users.Domain.Shared\Volo.Abp.Users.Domain.Shared.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Validation\Volo.Abp.Validation.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.Features\Volo.Abp.Features.csproj" />
</ItemGroup>
<ItemGroup>

@ -1,4 +1,5 @@
using Volo.Abp.Identity.Localization;
using Volo.Abp.Features;
using Volo.Abp.Identity.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity;
@ -11,7 +12,8 @@ namespace Volo.Abp.Identity
{
[DependsOn(
typeof(AbpUsersDomainSharedModule),
typeof(AbpValidationModule)
typeof(AbpValidationModule),
typeof(AbpFeaturesModule)
)]
public class AbpIdentityDomainSharedModule : AbpModule
{

@ -0,0 +1,9 @@
namespace Volo.Abp.Identity.Features
{
public class IdentityFeature
{
public const string GroupName = "Identity";
public const string TwoFactor = GroupName + ".TwoFactor";
}
}

@ -0,0 +1,23 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp.Features;
namespace Volo.Abp.Identity.Features
{
public static class IdentityFeatureCheckerExtensions
{
public static async Task<IdentityTwoFactorBehaviour> GetIdentityTwoFactorBehaviour([NotNull] this IFeatureChecker featureChecker)
{
Check.NotNull(featureChecker, nameof(featureChecker));
var value = await featureChecker.GetOrNullAsync(IdentityFeature.TwoFactor);
if (value.IsNullOrWhiteSpace() || !Enum.TryParse<IdentityTwoFactorBehaviour>(value, out var behaviour))
{
throw new AbpException($"{IdentityFeature.TwoFactor} feature value is invalid");
}
return behaviour;
}
}
}

@ -0,0 +1,51 @@
using System;
using Volo.Abp.Features;
using Volo.Abp.Identity.Localization;
using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.Identity.Features
{
public class IdentityFeatureDefinitionProvider : FeatureDefinitionProvider
{
public override void Define(IFeatureDefinitionContext context)
{
var group = context.AddGroup(IdentityFeature.GroupName, L("Feature:IdentityGroup"));
group.AddFeature(IdentityFeature.TwoFactor,
IdentityTwoFactorBehaviour.Optional.ToString(),
L("Feature:TwoFactor"),
L("Feature:TwoFactorDescription"),
new SelectionStringValueType
{
ItemSource = new StaticSelectionStringValueItemSource(
new LocalizableSelectionStringValueItem
{
Value = IdentityTwoFactorBehaviour.Optional.ToString(),
DisplayText = GetTwoFactorBehaviourLocalizableStringInfo("Feature:TwoFactor.Optional")
},
new LocalizableSelectionStringValueItem
{
Value = IdentityTwoFactorBehaviour.Disabled.ToString(),
DisplayText = GetTwoFactorBehaviourLocalizableStringInfo("Feature:TwoFactor.Disabled")
},
new LocalizableSelectionStringValueItem
{
Value = IdentityTwoFactorBehaviour.Forced.ToString(),
DisplayText = GetTwoFactorBehaviourLocalizableStringInfo("Feature:TwoFactor.Forced")
}
)
});
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<IdentityResource>(name);
}
private static LocalizableStringInfo GetTwoFactorBehaviourLocalizableStringInfo(string key)
{
return new LocalizableStringInfo(LocalizationResourceNameAttribute.GetName(typeof(IdentityResource)), key);
}
}
}

@ -0,0 +1,11 @@
namespace Volo.Abp.Identity.Features
{
public enum IdentityTwoFactorBehaviour
{
Optional,
Disabled,
Forced
}
}

@ -76,6 +76,12 @@
"Permission:ChangePermissions": "Change permissions",
"Permission:UserManagement": "User management",
"Permission:UserLookup": "User lookup",
"Feature:IdentityGroup": "Identity",
"Feature:TwoFactor": "Two Factor",
"Feature:TwoFactorDescription": "Two Factor",
"Feature:TwoFactor.Optional": "Optional",
"Feature:TwoFactor.Disabled": "Disabled",
"Feature:TwoFactor.Forced": "Forced",
"DisplayName:Abp.Identity.Password.RequiredLength": "Required length",
"DisplayName:Abp.Identity.Password.RequiredUniqueChars": "Required unique characters number",
"DisplayName:Abp.Identity.Password.RequireNonAlphanumeric": "Required non-alphanumeric character",
@ -103,6 +109,10 @@
"Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Whether the phoneNumber can be confirmed by the user.",
"Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Whether a confirmed telephone number is required to sign in.",
"Description:Abp.Identity.User.IsUserNameUpdateEnabled": "Whether the username can be updated by the user.",
"Description:Abp.Identity.User.IsEmailUpdateEnabled": "Whether the email can be updated by the user."
"Description:Abp.Identity.User.IsEmailUpdateEnabled": "Whether the email can be updated by the user.",
"DisplayName:Abp.Identity.TwoFactorBehaviour": "Two Factor behaviour",
"Description:Abp.Identity.TwoFactorBehaviour": "Two Factor behaviour",
"DisplayName:Abp.Identity.UsersCanChange": "Allow users to change their Two Factor.",
"Description:Abp.Identity.UsersCanChange": "Allow users to change their Two Factor."
}
}
}

@ -76,6 +76,12 @@
"Permission:ChangePermissions": "İzinleri değiştirme",
"Permission:UserManagement": "Kullanıcı yönetimi",
"Permission:UserLookup": "Kullanıcı sorgulama",
"Feature:IdentityGroup": "Kimlik",
"Feature:TwoFactor": "İki faktörlü kimlik doğrulama",
"Feature:TwoFactorDescription": "İki faktörlü kimlik doğrulama",
"Feature:TwoFactor.Optional": "İsteğe bağlı",
"Feature:TwoFactor.Disabled": "Devre dışı",
"Feature:TwoFactor.Forced": "Zorla etkinleştirildi",
"DisplayName:Abp.Identity.Password.RequiredLength": "Uzunluk gerekli",
"DisplayName:Abp.Identity.Password.RequiredUniqueChars": "Tekil karakter gerekli",
"DisplayName:Abp.Identity.Password.RequireNonAlphanumeric": "Alfasayısal olmayan karakter gerekli",
@ -103,6 +109,10 @@
"Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation": "Oturum açmak için telefon numarası gerekli",
"Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "Oturum açmak için onaylanmış bir telefon numarasının gerekli olup olmadığı.",
"Description:Abp.Identity.User.IsUserNameUpdateEnabled": "Kullanıcı adının, kullanıcının kendisi tarafından güncellenebilirliği.",
"Description:Abp.Identity.User.IsEmailUpdateEnabled": "E-posta alanının, kullanıcının kendisi tarafından güncellenebilirliği"
"Description:Abp.Identity.User.IsEmailUpdateEnabled": "E-posta alanının, kullanıcının kendisi tarafından güncellenebilirliği",
"DisplayName:Abp.Identity.TwoFactorBehaviour": "İki faktörlü kimlik doğrulama davranışı",
"Description:Abp.Identity.TwoFactorBehaviour": "İki faktörlü kimlik doğrulama davranışı",
"DisplayName:Abp.Identity.UsersCanChange": "Kullanıcıların faktör kimlik doğrulamasını değiştirmesine izin verin.",
"Description:Abp.Identity.UsersCanChange": "Kullanıcıların faktör kimlik doğrulamasını değiştirmesine izin verin."
}
}

@ -73,6 +73,12 @@
"Permission:ChangePermissions": "更改权限",
"Permission:UserManagement": "用户管理",
"Permission:UserLookup": "用户查询",
"Feature:IdentityGroup": "身份标识",
"Feature:TwoFactor": "双因素身份验证",
"Feature:TwoFactorDescription": "双因素身份验证",
"Feature:TwoFactor.Optional": "可选",
"Feature:TwoFactor.Disabled": "禁用",
"Feature:TwoFactor.Forced": "强制启用",
"DisplayName:Abp.Identity.Password.RequiredLength": "要求长度",
"DisplayName:Abp.Identity.Password.RequiredUniqueChars": "要求唯一字符数量",
"DisplayName:Abp.Identity.Password.RequireNonAlphanumeric": "要求非字母数字",
@ -98,6 +104,10 @@
"Description:Abp.Identity.SignIn.RequireConfirmedEmail": "登录时是否需要验证的电子邮箱.",
"Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber": "登录时是否需要验证的手机号码.",
"Description:Abp.Identity.User.IsUserNameUpdateEnabled": "是否允许用户更新用户名.",
"Description:Abp.Identity.User.IsEmailUpdateEnabled": "是否允许用户更新电子邮箱."
"Description:Abp.Identity.User.IsEmailUpdateEnabled": "是否允许用户更新电子邮箱.",
"DisplayName:Abp.Identity.TwoFactorBehaviour": "双因素身份验证行为",
"Description:Abp.Identity.TwoFactorBehaviour": "双因素身份验证行为",
"DisplayName:Abp.Identity.UsersCanChange": "允许用户更改其因素身份验证.",
"Description:Abp.Identity.UsersCanChange": "允许用户更改其因素身份验证."
}
}

@ -48,5 +48,14 @@
public const string MaxUserMembershipCount = OrganizationUnitPrefix + ".MaxUserMembershipCount";
}
public static class TwoFactor
{
private const string TwoFactorPrefix = Prefix + ".TwoFactor";
public const string Behaviour = TwoFactorPrefix + ".Behaviour";
public const string UsersCanChange = TwoFactorPrefix + ".UsersCanChange";
}
}
}
}

@ -1,4 +1,5 @@
using Volo.Abp.Identity.Localization;
using Volo.Abp.Identity.Features;
using Volo.Abp.Identity.Localization;
using Volo.Abp.Identity.Settings;
using Volo.Abp.Localization;
using Volo.Abp.Settings;
@ -33,85 +34,97 @@ namespace Volo.Abp.Identity
new SettingDefinition(
IdentitySettingNames.Password.RequireLowercase,
true.ToString(),
true.ToString(),
L("DisplayName:Abp.Identity.Password.RequireLowercase"),
L("Description:Abp.Identity.Password.RequireLowercase"),
true),
new SettingDefinition(
IdentitySettingNames.Password.RequireUppercase,
true.ToString(),
true.ToString(),
L("DisplayName:Abp.Identity.Password.RequireUppercase"),
L("Description:Abp.Identity.Password.RequireUppercase"),
true),
new SettingDefinition(
IdentitySettingNames.Password.RequireDigit,
true.ToString(),
true.ToString(),
L("DisplayName:Abp.Identity.Password.RequireDigit"),
L("Description:Abp.Identity.Password.RequireDigit"),
true),
new SettingDefinition(
IdentitySettingNames.Lockout.AllowedForNewUsers,
true.ToString(),
true.ToString(),
L("DisplayName:Abp.Identity.Lockout.AllowedForNewUsers"),
L("Description:Abp.Identity.Lockout.AllowedForNewUsers"),
true),
new SettingDefinition(
IdentitySettingNames.Lockout.LockoutDuration,
(5 * 60).ToString(),
(5 * 60).ToString(),
L("DisplayName:Abp.Identity.Lockout.LockoutDuration"),
L("Description:Abp.Identity.Lockout.LockoutDuration"),
true),
new SettingDefinition(
IdentitySettingNames.Lockout.MaxFailedAccessAttempts,
5.ToString(),
5.ToString(),
L("DisplayName:Abp.Identity.Lockout.MaxFailedAccessAttempts"),
L("Description:Abp.Identity.Lockout.MaxFailedAccessAttempts"),
true),
new SettingDefinition(
IdentitySettingNames.SignIn.RequireConfirmedEmail,
false.ToString(),
false.ToString(),
L("DisplayName:Abp.Identity.SignIn.RequireConfirmedEmail"),
L("Description:Abp.Identity.SignIn.RequireConfirmedEmail"),
true),
new SettingDefinition(
IdentitySettingNames.SignIn.EnablePhoneNumberConfirmation,
true.ToString(),
L("DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation"),
L("Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation"),
IdentitySettingNames.SignIn.EnablePhoneNumberConfirmation,
true.ToString(),
L("DisplayName:Abp.Identity.SignIn.EnablePhoneNumberConfirmation"),
L("Description:Abp.Identity.SignIn.EnablePhoneNumberConfirmation"),
true),
new SettingDefinition(
IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber,
false.ToString(),
L("DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber"),
L("Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber"),
IdentitySettingNames.SignIn.RequireConfirmedPhoneNumber,
false.ToString(),
L("DisplayName:Abp.Identity.SignIn.RequireConfirmedPhoneNumber"),
L("Description:Abp.Identity.SignIn.RequireConfirmedPhoneNumber"),
true),
new SettingDefinition(
IdentitySettingNames.User.IsUserNameUpdateEnabled,
true.ToString(),
true.ToString(),
L("DisplayName:Abp.Identity.User.IsUserNameUpdateEnabled"),
L("Description:Abp.Identity.User.IsUserNameUpdateEnabled"),
true),
new SettingDefinition(
IdentitySettingNames.User.IsEmailUpdateEnabled,
true.ToString(),
true.ToString(),
L("DisplayName:Abp.Identity.User.IsEmailUpdateEnabled"),
L("Description:Abp.Identity.User.IsEmailUpdateEnabled"),
true),
new SettingDefinition(
new SettingDefinition(
IdentitySettingNames.OrganizationUnit.MaxUserMembershipCount,
int.MaxValue.ToString(),
int.MaxValue.ToString(),
L("Identity.OrganizationUnit.MaxUserMembershipCount"),
L("Identity.OrganizationUnit.MaxUserMembershipCount"),
true)
true),
new SettingDefinition(IdentitySettingNames.TwoFactor.Behaviour,
IdentityTwoFactorBehaviour.Optional.ToString(),
L("DisplayName:Abp.Identity.TwoFactorBehaviour"),
L("Description:Abp.Identity.TwoFactorBehaviour"),
isVisibleToClients: true),
new SettingDefinition(IdentitySettingNames.TwoFactor.UsersCanChange,
true.ToString(),
L("DisplayName:Abp.Identity.UsersCanChange"),
L("Description:Abp.Identity.UsersCanChange"),
isVisibleToClients: true)
);
}

@ -0,0 +1,25 @@
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Volo.Abp.Identity.Features;
using Volo.Abp.Identity.Settings;
using Volo.Abp.Settings;
namespace Volo.Abp.Identity
{
public static class IdentitySettingProviderExtensions
{
public static async Task<IdentityTwoFactorBehaviour> GetIdentityTwoFactorBehaviour([NotNull] this ISettingProvider settingProvider)
{
Check.NotNull(settingProvider, nameof(settingProvider));
var value = await settingProvider.GetOrNullAsync(IdentitySettingNames.TwoFactor.Behaviour);
if (value.IsNullOrWhiteSpace() || !Enum.TryParse<IdentityTwoFactorBehaviour>(value, out var behaviour))
{
throw new AbpException($"{IdentitySettingNames.TwoFactor.Behaviour} setting value is invalid");
}
return behaviour;
}
}
}

@ -0,0 +1,69 @@
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Volo.Abp.Features;
using Volo.Abp.Identity.Features;
using Volo.Abp.Settings;
namespace Volo.Abp.Identity
{
public class IdentityTwoFactorManager : IDomainService
{
protected IFeatureChecker FeatureChecker { get; }
protected ISettingProvider SettingProvider { get; }
public IdentityTwoFactorManager(IFeatureChecker featureChecker, ISettingProvider settingProvider)
{
FeatureChecker = featureChecker;
SettingProvider = settingProvider;
}
public virtual async Task<bool> IsOptionalAsync()
{
var feature = await FeatureChecker.GetIdentityTwoFactorBehaviour();
if (feature == IdentityTwoFactorBehaviour.Optional)
{
var setting = await SettingProvider.GetIdentityTwoFactorBehaviour();
if (setting == IdentityTwoFactorBehaviour.Optional)
{
return true;
}
}
return false;
}
public virtual async Task<bool> IsForcedEnableAsync()
{
var feature = await FeatureChecker.GetIdentityTwoFactorBehaviour();
if (feature == IdentityTwoFactorBehaviour.Forced)
{
return true;
}
var setting = await SettingProvider.GetIdentityTwoFactorBehaviour();
if (setting == IdentityTwoFactorBehaviour.Forced)
{
return true;
}
return false;
}
public virtual async Task<bool> IsForcedDisableAsync()
{
var feature = await FeatureChecker.GetIdentityTwoFactorBehaviour();
if (feature == IdentityTwoFactorBehaviour.Disabled)
{
return true;
}
var setting = await SettingProvider.GetIdentityTwoFactorBehaviour();
if (setting == IdentityTwoFactorBehaviour.Disabled)
{
return true;
}
return false;
}
}
}

@ -11,7 +11,10 @@ using Microsoft.Extensions.Logging;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Features;
using Volo.Abp.Guids;
using Volo.Abp.Identity.Features;
using Volo.Abp.Settings;
namespace Volo.Abp.Identity
{
@ -56,12 +59,17 @@ namespace Volo.Abp.Identity
protected ILookupNormalizer LookupNormalizer { get; }
protected IIdentityUserRepository UserRepository { get; }
protected IFeatureChecker FeatureChecker { get; }
protected ISettingProvider SettingProvider { get; }
public IdentityUserStore(
IIdentityUserRepository userRepository,
IIdentityRoleRepository roleRepository,
IGuidGenerator guidGenerator,
ILogger<IdentityRoleStore> logger,
ILookupNormalizer lookupNormalizer,
IFeatureChecker featureChecker,
ISettingProvider settingProvider,
IdentityErrorDescriber describer = null)
{
UserRepository = userRepository;
@ -69,6 +77,8 @@ namespace Volo.Abp.Identity
GuidGenerator = guidGenerator;
Logger = logger;
LookupNormalizer = lookupNormalizer;
FeatureChecker = featureChecker;
SettingProvider = settingProvider;
ErrorDescriber = describer ?? new IdentityErrorDescriber();
}
@ -931,13 +941,33 @@ namespace Volo.Abp.Identity
/// The <see cref="Task"/> that represents the asynchronous operation, containing a flag indicating whether the specified
/// <paramref name="user"/> has two factor authentication enabled or not.
/// </returns>
public virtual Task<bool> GetTwoFactorEnabledAsync([NotNull] IdentityUser user, CancellationToken cancellationToken = default)
public virtual async Task<bool> GetTwoFactorEnabledAsync([NotNull] IdentityUser user, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
Check.NotNull(user, nameof(user));
return Task.FromResult(user.TwoFactorEnabled);
var feature = await FeatureChecker.GetIdentityTwoFactorBehaviour();
if (feature == IdentityTwoFactorBehaviour.Disabled)
{
return false;
}
if (feature == IdentityTwoFactorBehaviour.Forced)
{
return true;
}
var setting = await SettingProvider.GetIdentityTwoFactorBehaviour();
if (setting == IdentityTwoFactorBehaviour.Disabled)
{
return false;
}
if (setting == IdentityTwoFactorBehaviour.Forced)
{
return true;
}
return user.TwoFactorEnabled;
}
/// <summary>

Loading…
Cancel
Save