From a36fda11e3914e13f472938d57865ff104ba6aa9 Mon Sep 17 00:00:00 2001
From: maliming <6908465+maliming@users.noreply.github.com>
Date: Wed, 24 Jun 2020 17:28:02 +0800
Subject: [PATCH 01/32] Init UserSecurityLog.
---
.../Volo.Abp.Security.csproj | 1 +
.../SecurityLog/IUserSecurityLogStore.cs | 9 ++++
.../Users/SecurityLog/UserSecurityLogInfo.cs | 53 +++++++++++++++++++
3 files changed, 63 insertions(+)
create mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/IUserSecurityLogStore.cs
create mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/UserSecurityLogInfo.cs
diff --git a/framework/src/Volo.Abp.Security/Volo.Abp.Security.csproj b/framework/src/Volo.Abp.Security/Volo.Abp.Security.csproj
index 65aca85c88..6ffaac1232 100644
--- a/framework/src/Volo.Abp.Security/Volo.Abp.Security.csproj
+++ b/framework/src/Volo.Abp.Security/Volo.Abp.Security.csproj
@@ -16,6 +16,7 @@
+
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/IUserSecurityLogStore.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/IUserSecurityLogStore.cs
new file mode 100644
index 0000000000..18d5b1c23e
--- /dev/null
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/IUserSecurityLogStore.cs
@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.Users.SecurityLog
+{
+ public interface IUserSecurityLogStore
+ {
+ Task SaveAsync(UserSecurityLogInfo userSecurityLogInfo);
+ }
+}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/UserSecurityLogInfo.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/UserSecurityLogInfo.cs
new file mode 100644
index 0000000000..5ecb8d9314
--- /dev/null
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/UserSecurityLogInfo.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using Volo.Abp.Data;
+
+namespace Volo.Abp.Users.SecurityLog
+{
+ [Serializable]
+ public class UserSecurityLogInfo : IHasExtraProperties
+ {
+ ///
+ /// The name of the application or service writing user security logs.
+ /// Default: null.
+ ///
+ public string ApplicationName { get; set; }
+
+ ///
+ /// Web, JWT, Identity, Identity_Server
+ ///
+ public string Identity { get; set; }
+
+ ///
+ /// login_successful, login_failed, logout, change_pwd, refresh_token...
+ ///
+ public string Action { get; set; }
+
+ public Dictionary ExtraProperties { get; }
+
+ public Guid? UserId { get; set; }
+
+ public string UserName { get; set; }
+
+ public Guid? TenantId { get; set; }
+
+ public string TenantName { get; set; }
+
+ public string ClientId { get; set; }
+
+ public string ClientName { get; set; }
+
+ public string CorrelationId { get; set; }
+
+ public string ClientIpAddress { get; set; }
+
+ public string BrowserInfo { get; set; }
+
+ public DateTime CreationTime { get; set; }
+
+ public UserSecurityLogInfo()
+ {
+ ExtraProperties = new Dictionary();
+ }
+ }
+}
From 58f0204e7b80eab68649dbe8d1387e8f92314c03 Mon Sep 17 00:00:00 2001
From: maliming <6908465+maliming@users.noreply.github.com>
Date: Mon, 6 Jul 2020 10:31:58 +0800
Subject: [PATCH 02/32] Try to set GUID Id in the InsertAsync method of the EF
Core repository.
Resolve #4631
---
.../EntityFrameworkCore/EfCoreRepository.cs | 38 ++++++++++++++++---
.../Repositories/MongoDB/MongoDbRepository.cs | 1 +
2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
index a48e5d7da5..6cb761eeef 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
@@ -10,6 +10,7 @@ using Microsoft.Extensions.Options;
using Volo.Abp.Domain.Entities;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.DependencyInjection;
+using Volo.Abp.Guids;
namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
{
@@ -28,9 +29,12 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
private readonly IDbContextProvider _dbContextProvider;
private readonly Lazy> _entityOptionsLazy;
+ protected virtual IGuidGenerator GuidGenerator { get; set; }
+
public EfCoreRepository(IDbContextProvider dbContextProvider)
{
_dbContextProvider = dbContextProvider;
+ GuidGenerator = SimpleGuidGenerator.Instance;
_entityOptionsLazy = new Lazy>(
() => ServiceProvider
@@ -39,9 +43,11 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
.GetOrNull() ?? AbpEntityOptions.Empty
);
}
-
+
public override async Task InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
+ CheckAndSetId(entity);
+
var savedEntity = DbSet.Add(entity).Entity;
if (autoSave)
@@ -65,7 +71,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
return updatedEntity;
}
-
+
public override async Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
DbSet.Remove(entity);
@@ -94,7 +100,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
}
public override async Task FindAsync(
- Expression> predicate,
+ Expression> predicate,
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
@@ -172,16 +178,38 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
return query;
}
+
+ protected virtual void CheckAndSetId(TEntity entity)
+ {
+ if (entity is IEntity entityWithGuidId)
+ {
+ TrySetGuidId(entity, entityWithGuidId);
+ }
+ }
+
+ protected virtual void TrySetGuidId(TEntity entry, IEntity entity)
+ {
+ if (entity.Id != default)
+ {
+ return;
+ }
+
+ EntityHelper.TrySetId(
+ entity,
+ () => GuidGenerator.Create(),
+ true
+ );
+ }
}
- public class EfCoreRepository : EfCoreRepository,
+ public class EfCoreRepository : EfCoreRepository,
IEfCoreRepository,
ISupportsExplicitLoading
where TDbContext : IEfCoreDbContext
where TEntity : class, IEntity
{
- public EfCoreRepository(IDbContextProvider dbContextProvider)
+ public EfCoreRepository(IDbContextProvider dbContextProvider)
: base(dbContextProvider)
{
diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
index 6abcc70f0d..035de803af 100644
--- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
+++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
@@ -49,6 +49,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
LocalEventBus = NullLocalEventBus.Instance;
DistributedEventBus = NullDistributedEventBus.Instance;
EntityChangeEventHelper = NullEntityChangeEventHelper.Instance;
+ GuidGenerator = SimpleGuidGenerator.Instance;
}
public override async Task InsertAsync(
From 4af6d60a59441cd009fae235e62fb54470b9bba1 Mon Sep 17 00:00:00 2001
From: maliming <6908465+maliming@users.noreply.github.com>
Date: Mon, 6 Jul 2020 10:38:22 +0800
Subject: [PATCH 03/32] Insert_Should_Set_Guid_Id unit test.
---
.../EntityFrameworkCore/EfCoreRepository.cs | 4 ++--
.../Abp/MongoDB/Repositories/Repository_Basic_Tests.cs | 10 ++++++++++
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
index 6cb761eeef..735353a454 100644
--- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
+++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
@@ -183,11 +183,11 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
{
if (entity is IEntity entityWithGuidId)
{
- TrySetGuidId(entity, entityWithGuidId);
+ TrySetGuidId(entityWithGuidId);
}
}
- protected virtual void TrySetGuidId(TEntity entry, IEntity entity)
+ protected virtual void TrySetGuidId(IEntity entity)
{
if (entity.Id != default)
{
diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs
index 09b1551e3d..a730fa4ecc 100644
--- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs
+++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs
@@ -64,5 +64,15 @@ namespace Volo.Abp.MongoDB.Repositories
person.Phones.Count.ShouldBe(1);
person.Phones.Any(p => p.PersonId == person.Id && p.Number == "1234567890").ShouldBeTrue();
}
+
+ [Fact]
+ public async Task Insert_Should_Set_Guid_Id()
+ {
+ var person = new Person(Guid.Empty, "New Person", 35);
+
+ await PersonRepository.InsertAsync(person);
+
+ person.Id.ShouldNotBe(Guid.Empty);
+ }
}
}
From ecd69f333a40a17b74ee960f4c8ae473be678d55 Mon Sep 17 00:00:00 2001
From: maliming <6908465+maliming@users.noreply.github.com>
Date: Mon, 6 Jul 2020 14:20:16 +0800
Subject: [PATCH 04/32] Add dataFormat option to the datatables column
definitions.
Resolve #4629
---
.../datatables/datatables-extensions.js | 26 +++++++++++++------
1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js
index 824e376934..dd749d0bc1 100644
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js
+++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js
@@ -299,6 +299,11 @@
column.targets = i;
}
+ if (!column.render && column.dataFormat){
+ var render = datatables.defaultRenderers[column.dataFormat];
+ column.render = render ? render : ISOStringToDateTimeLocaleString(column.dataFormat);
+ }
+
if (column.rowAction) {
customizeRowActionColumn(column);
}
@@ -330,18 +335,23 @@
}
};
+ var ISOStringToDateTimeLocaleString = function (format) {
+ return function(data) {
+ var date = luxon
+ .DateTime
+ .fromISO(data, {
+ locale: abp.localization.currentCulture.name
+ });
+ return format ? date.toLocaleString(format) : date.toLocaleString();
+ };
+ };
+
datatables.defaultRenderers['date'] = function (value) {
- return luxon
- .DateTime
- .fromISO(value, { locale: abp.localization.currentCulture.name })
- .toLocaleString();
+ return (ISOStringToDateTimeLocaleString())(value);
};
datatables.defaultRenderers['datetime'] = function (value) {
- return luxon
- .DateTime
- .fromISO(value, { locale: abp.localization.currentCulture.name })
- .toLocaleString(luxon.DateTime.DATETIME_SHORT);
+ return (ISOStringToDateTimeLocaleString(luxon.DateTime.DATETIME_SHORT))(value);
};
/************************************************************************
From 65957cbb6e2fa3c1d5b5a2c5d14cb527289a23be Mon Sep 17 00:00:00 2001
From: maliming <6908465+maliming@users.noreply.github.com>
Date: Wed, 8 Jul 2020 17:34:33 +0800
Subject: [PATCH 05/32] Add some infrastructure for security logs.
---
.../Auditing/AspNetCoreAuditLogContributor.cs | 24 +-----
.../AspNetCoreSecurityLogManager.cs | 74 +++++++++++++++++++
.../HttpContextWebClientInfoProvider.cs | 43 +++++++++++
.../WebClientInfo/IWebClientInfoProvider.cs | 9 +++
.../Abp/SecurityLog/AbpSecurityLogOptions.cs | 21 ++++++
.../SecurityLog/DefaultSecurityLogManager.cs | 34 +++++++++
.../Abp/SecurityLog/ISecurityLogManager.cs | 11 +++
.../Volo/Abp/SecurityLog/ISecurityLogStore.cs | 9 +++
.../SecurityLogInfo.cs} | 11 ++-
.../Abp/SecurityLog/SimpleSecurityLogStore.cs | 22 ++++++
.../SecurityLog/IUserSecurityLogStore.cs | 9 ---
.../IdentityServerSupportedLoginModel.cs | 3 +
.../Account/Controllers/AccountController.cs | 25 ++++++-
.../Pages/Account/AccountPageModel.cs | 11 +++
.../Pages/Account/Login.cshtml.cs | 8 +-
15 files changed, 277 insertions(+), 37 deletions(-)
create mode 100644 framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/SecurityLog/AspNetCoreSecurityLogManager.cs
create mode 100644 framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/HttpContextWebClientInfoProvider.cs
create mode 100644 framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/IWebClientInfoProvider.cs
create mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/AbpSecurityLogOptions.cs
create mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/DefaultSecurityLogManager.cs
create mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogManager.cs
create mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogStore.cs
rename framework/src/Volo.Abp.Security/Volo/Abp/{Users/SecurityLog/UserSecurityLogInfo.cs => SecurityLog/SecurityLogInfo.cs} (82%)
create mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/SimpleSecurityLogStore.cs
delete mode 100644 framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/IUserSecurityLogStore.cs
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs
index 378b92a14a..04238a9a45 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs
+++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/Auditing/AspNetCoreAuditLogContributor.cs
@@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
+using Volo.Abp.AspNetCore.WebClientInfo;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
@@ -35,14 +36,15 @@ namespace Volo.Abp.AspNetCore.Auditing
context.AuditInfo.Url = BuildUrl(httpContext);
}
+ var clientInfoProvider = context.ServiceProvider.GetRequiredService();
if (context.AuditInfo.ClientIpAddress == null)
{
- context.AuditInfo.ClientIpAddress = GetClientIpAddress(httpContext);
+ context.AuditInfo.ClientIpAddress = clientInfoProvider.ClientIpAddress;
}
if (context.AuditInfo.BrowserInfo == null)
{
- context.AuditInfo.BrowserInfo = GetBrowserInfo(httpContext);
+ context.AuditInfo.BrowserInfo = clientInfoProvider.BrowserInfo;
}
//TODO: context.AuditInfo.ClientName
@@ -62,24 +64,6 @@ namespace Volo.Abp.AspNetCore.Auditing
}
}
- protected virtual string GetBrowserInfo(HttpContext httpContext)
- {
- return httpContext.Request?.Headers?["User-Agent"];
- }
-
- protected virtual string GetClientIpAddress(HttpContext httpContext)
- {
- try
- {
- return httpContext.Connection?.RemoteIpAddress?.ToString();
- }
- catch (Exception ex)
- {
- Logger.LogException(ex, LogLevel.Warning);
- return null;
- }
- }
-
protected virtual string BuildUrl(HttpContext httpContext)
{
//TODO: Add options to include/exclude query, schema and host
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/SecurityLog/AspNetCoreSecurityLogManager.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/SecurityLog/AspNetCoreSecurityLogManager.cs
new file mode 100644
index 0000000000..4c60a6ce37
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/SecurityLog/AspNetCoreSecurityLogManager.cs
@@ -0,0 +1,74 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Volo.Abp.AspNetCore.WebClientInfo;
+using Volo.Abp.Clients;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.SecurityLog;
+using Volo.Abp.Timing;
+using Volo.Abp.Tracing;
+using Volo.Abp.Users;
+
+namespace Volo.Abp.AspNetCore.SecurityLog
+{
+ [Dependency(ReplaceServices = true)]
+ public class AspNetCoreSecurityLogManager : DefaultSecurityLogManager
+ {
+ protected ILogger Logger { get; }
+ protected IClock Clock { get; }
+ protected ICurrentUser CurrentUser { get; }
+ protected ICurrentTenant CurrentTenant { get; }
+ protected ICurrentClient CurrentClient { get; }
+ protected IHttpContextAccessor HttpContextAccessor { get; }
+ protected ICorrelationIdProvider CorrelationIdProvider { get; }
+
+ protected IWebClientInfoProvider WebClientInfoProvider { get; }
+
+ public AspNetCoreSecurityLogManager(
+ IOptions securityLogOptions,
+ ISecurityLogStore securityLogStore,
+ ILogger logger,
+ IClock clock,
+ ICurrentUser currentUser,
+ ICurrentTenant currentTenant,
+ ICurrentClient currentClient,
+ IHttpContextAccessor httpContextAccessor,
+ ICorrelationIdProvider correlationIdProvider,
+ IWebClientInfoProvider webClientInfoProvider)
+ : base(securityLogOptions, securityLogStore)
+ {
+ Logger = logger;
+ Clock = clock;
+ CurrentUser = currentUser;
+ CurrentTenant = currentTenant;
+ CurrentClient = currentClient;
+ HttpContextAccessor = httpContextAccessor;
+ CorrelationIdProvider = correlationIdProvider;
+ WebClientInfoProvider = webClientInfoProvider;
+ }
+
+ public override async Task CreateAsync()
+ {
+ var securityLogInfo = await base.CreateAsync();
+
+ securityLogInfo.CreationTime = Clock.Now;
+
+ securityLogInfo.TenantId = CurrentTenant.Id;
+ securityLogInfo.TenantName = CurrentTenant.Name;
+
+ securityLogInfo.UserId = CurrentUser.Id;
+ securityLogInfo.UserName = CurrentUser.UserName;
+
+ securityLogInfo.ClientId = CurrentClient.Id;
+
+ securityLogInfo.CorrelationId = CorrelationIdProvider.Get();
+
+ securityLogInfo.ClientIpAddress = WebClientInfoProvider.ClientIpAddress;
+ securityLogInfo.BrowserInfo = WebClientInfoProvider.BrowserInfo;
+
+ return securityLogInfo;
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/HttpContextWebClientInfoProvider.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/HttpContextWebClientInfoProvider.cs
new file mode 100644
index 0000000000..503771c938
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/HttpContextWebClientInfoProvider.cs
@@ -0,0 +1,43 @@
+using System;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.AspNetCore.WebClientInfo
+{
+ public class HttpContextWebClientInfoProvider : IWebClientInfoProvider, ITransientDependency
+ {
+ protected ILogger Logger { get; }
+ protected IHttpContextAccessor HttpContextAccessor { get; }
+
+ public HttpContextWebClientInfoProvider(
+ ILogger logger,
+ IHttpContextAccessor httpContextAccessor)
+ {
+ Logger = logger;
+ HttpContextAccessor = httpContextAccessor;
+ }
+
+ public string BrowserInfo => GetBrowserInfo();
+
+ public string ClientIpAddress => GetClientIpAddress();
+
+ protected virtual string GetBrowserInfo()
+ {
+ return HttpContextAccessor.HttpContext?.Request?.Headers?["User-Agent"];
+ }
+
+ protected virtual string GetClientIpAddress()
+ {
+ try
+ {
+ return HttpContextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString();
+ }
+ catch (Exception ex)
+ {
+ Logger.LogException(ex, LogLevel.Warning);
+ return null;
+ }
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/IWebClientInfoProvider.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/IWebClientInfoProvider.cs
new file mode 100644
index 0000000000..3a15ac2f93
--- /dev/null
+++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/WebClientInfo/IWebClientInfoProvider.cs
@@ -0,0 +1,9 @@
+namespace Volo.Abp.AspNetCore.WebClientInfo
+{
+ public interface IWebClientInfoProvider
+ {
+ string BrowserInfo { get; }
+
+ string ClientIpAddress { get; }
+ }
+}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/AbpSecurityLogOptions.cs b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/AbpSecurityLogOptions.cs
new file mode 100644
index 0000000000..a1cc6406f4
--- /dev/null
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/AbpSecurityLogOptions.cs
@@ -0,0 +1,21 @@
+namespace Volo.Abp.SecurityLog
+{
+ public class AbpSecurityLogOptions
+ {
+ ///
+ /// Default: true.
+ ///
+ public bool IsEnabled { get; set; }
+
+ ///
+ /// The name of the application or service writing security log.
+ /// Default: null.
+ ///
+ public string ApplicationName { get; set; }
+
+ public AbpSecurityLogOptions()
+ {
+ IsEnabled = true;
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/DefaultSecurityLogManager.cs b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/DefaultSecurityLogManager.cs
new file mode 100644
index 0000000000..0f5d40567c
--- /dev/null
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/DefaultSecurityLogManager.cs
@@ -0,0 +1,34 @@
+using System.Threading.Tasks;
+using Microsoft.Extensions.Options;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.SecurityLog
+{
+ public class DefaultSecurityLogManager : ISecurityLogManager, ITransientDependency
+ {
+ protected AbpSecurityLogOptions SecurityLogOptions { get; }
+
+ protected ISecurityLogStore SecurityLogStore { get; }
+
+ public DefaultSecurityLogManager(
+ IOptions securityLogOptions,
+ ISecurityLogStore securityLogStore)
+ {
+ SecurityLogStore = securityLogStore;
+ SecurityLogOptions = securityLogOptions.Value;
+ }
+
+ public virtual Task CreateAsync()
+ {
+ return Task.FromResult(new SecurityLogInfo
+ {
+ ApplicationName = SecurityLogOptions.ApplicationName
+ });
+ }
+
+ public async Task SaveAsync(SecurityLogInfo securityLogInfo)
+ {
+ await SecurityLogStore.SaveAsync(securityLogInfo);
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogManager.cs b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogManager.cs
new file mode 100644
index 0000000000..f762522509
--- /dev/null
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogManager.cs
@@ -0,0 +1,11 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.SecurityLog
+{
+ public interface ISecurityLogManager
+ {
+ Task CreateAsync();
+
+ Task SaveAsync(SecurityLogInfo securityLogInfo);
+ }
+}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogStore.cs b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogStore.cs
new file mode 100644
index 0000000000..df2496f307
--- /dev/null
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogStore.cs
@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.SecurityLog
+{
+ public interface ISecurityLogStore
+ {
+ Task SaveAsync(SecurityLogInfo securityLogInfo);
+ }
+}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/UserSecurityLogInfo.cs b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/SecurityLogInfo.cs
similarity index 82%
rename from framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/UserSecurityLogInfo.cs
rename to framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/SecurityLogInfo.cs
index 5ecb8d9314..11f46c7527 100644
--- a/framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/UserSecurityLogInfo.cs
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/SecurityLogInfo.cs
@@ -2,10 +2,10 @@
using System.Collections.Generic;
using Volo.Abp.Data;
-namespace Volo.Abp.Users.SecurityLog
+namespace Volo.Abp.SecurityLog
{
[Serializable]
- public class UserSecurityLogInfo : IHasExtraProperties
+ public class SecurityLogInfo : IHasExtraProperties
{
///
/// The name of the application or service writing user security logs.
@@ -45,9 +45,14 @@ namespace Volo.Abp.Users.SecurityLog
public DateTime CreationTime { get; set; }
- public UserSecurityLogInfo()
+ public SecurityLogInfo()
{
ExtraProperties = new Dictionary();
}
+
+ public override string ToString()
+ {
+ return $"SECURITY LOG: [{ApplicationName} - {Identity} - {Action}]";
+ }
}
}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/SimpleSecurityLogStore.cs b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/SimpleSecurityLogStore.cs
new file mode 100644
index 0000000000..bc9c22c4df
--- /dev/null
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/SimpleSecurityLogStore.cs
@@ -0,0 +1,22 @@
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.SecurityLog
+{
+ public class SimpleSecurityLogStore : ISecurityLogStore, ITransientDependency
+ {
+ public ILogger Logger { get; set; }
+
+ public SimpleSecurityLogStore(ILogger logger)
+ {
+ Logger = logger;
+ }
+
+ public Task SaveAsync(SecurityLogInfo securityLogInfo)
+ {
+ Logger.LogInformation(securityLogInfo.ToString());
+ return Task.FromResult(0);
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/IUserSecurityLogStore.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/IUserSecurityLogStore.cs
deleted file mode 100644
index 18d5b1c23e..0000000000
--- a/framework/src/Volo.Abp.Security/Volo/Abp/Users/SecurityLog/IUserSecurityLogStore.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System.Threading.Tasks;
-
-namespace Volo.Abp.Users.SecurityLog
-{
- public interface IUserSecurityLogStore
- {
- Task SaveAsync(UserSecurityLogInfo userSecurityLogInfo);
- }
-}
diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
index 60f55497da..af157ed629 100644
--- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
+++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
@@ -15,6 +15,7 @@ using System.Threading.Tasks;
using Volo.Abp.Account.Settings;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
+using Volo.Abp.SecurityLog;
using Volo.Abp.Settings;
using Volo.Abp.Uow;
@@ -127,6 +128,8 @@ namespace Volo.Abp.Account.Web.Pages.Account
true
);
+ await CreateSecurityLog("Login_" + result);
+
if (result.RequiresTwoFactor)
{
return RedirectToPage("./SendSecurityCode", new
diff --git a/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs b/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs
index 64fbdd40d1..38e8e7e7c2 100644
--- a/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs
+++ b/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs
@@ -7,6 +7,7 @@ using Volo.Abp.Account.Settings;
using Volo.Abp.Account.Web.Areas.Account.Controllers.Models;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Identity;
+using Volo.Abp.SecurityLog;
using Volo.Abp.Settings;
using Volo.Abp.Validation;
using SignInResult = Microsoft.AspNetCore.Identity.SignInResult;
@@ -25,14 +26,20 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers
protected SignInManager SignInManager { get; }
protected IdentityUserManager UserManager { get; }
protected ISettingProvider SettingProvider { get; }
+ protected ISecurityLogManager SecurityLogManager { get; }
- public AccountController(SignInManager signInManager, IdentityUserManager userManager, ISettingProvider settingProvider)
+ public AccountController(
+ SignInManager signInManager,
+ IdentityUserManager userManager,
+ ISettingProvider settingProvider,
+ ISecurityLogManager securityLogManager)
{
LocalizationResource = typeof(AccountResource);
SignInManager = signInManager;
UserManager = userManager;
SettingProvider = settingProvider;
+ SecurityLogManager = securityLogManager;
}
[HttpPost]
@@ -44,19 +51,23 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers
ValidateLoginInfo(login);
await ReplaceEmailToUsernameOfInputIfNeeds(login);
-
- return GetAbpLoginResult(await SignInManager.PasswordSignInAsync(
+ var loginResult = GetAbpLoginResult(await SignInManager.PasswordSignInAsync(
login.UserNameOrEmailAddress,
login.Password,
login.RememberMe,
true
));
+
+ await CreateSecurityLog("Login_" + loginResult.Result);
+
+ return loginResult;
}
[HttpGet]
[Route("logout")]
public virtual async Task Logout()
{
+ await CreateSecurityLog("Logout");
await SignInManager.SignOutAsync();
}
@@ -150,5 +161,13 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers
throw new UserFriendlyException(L["LocalLoginDisabledMessage"]);
}
}
+
+ protected virtual async Task CreateSecurityLog(string action)
+ {
+ var securityLog = await SecurityLogManager.CreateAsync();
+ securityLog.Identity = "Web";
+ securityLog.Action = action;
+ await SecurityLogManager.SaveAsync(securityLog);
+ }
}
}
diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs
index 41bdd93647..d7fd0f8239 100644
--- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs
+++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs
@@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Account.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Abp.Identity;
+using Volo.Abp.SecurityLog;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace Volo.Abp.Account.Web.Pages.Account
@@ -15,6 +17,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
{
public SignInManager SignInManager { get; set; }
public IdentityUserManager UserManager { get; set; }
+ public ISecurityLogManager SecurityLogManager { get; }
protected AccountPageModel()
{
@@ -76,5 +79,13 @@ namespace Volo.Abp.Account.Web.Pages.Account
{
return "~/"; //TODO: ???
}
+
+ protected virtual async Task CreateSecurityLog(string action)
+ {
+ var securityLog = await SecurityLogManager.CreateAsync();
+ securityLog.Identity = "Web";
+ securityLog.Action = action;
+ await SecurityLogManager.SaveAsync(securityLog);
+ }
}
}
diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
index 5251cb12f1..2916e60b4f 100644
--- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
+++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
@@ -14,8 +14,8 @@ using Volo.Abp.Account.Settings;
using Volo.Abp.Auditing;
using Volo.Abp.Identity;
using Volo.Abp.Security.Claims;
+using Volo.Abp.SecurityLog;
using Volo.Abp.Settings;
-using Volo.Abp.Uow;
using Volo.Abp.Validation;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
@@ -83,7 +83,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
ValidateModel();
ExternalProviders = await GetExternalProviders();
-
+
EnableLocalLogin = await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin);
await ReplaceEmailToUsernameOfInputIfNeeds();
@@ -95,6 +95,8 @@ namespace Volo.Abp.Account.Web.Pages.Account
true
);
+ await CreateSecurityLog(result.ToString());
+
if (result.RequiresTwoFactor)
{
return RedirectToPage("./SendSecurityCode", new
@@ -182,6 +184,8 @@ namespace Volo.Abp.Account.Web.Pages.Account
bypassTwoFactor: true
);
+ await CreateSecurityLog(result.ToString());
+
if (result.IsLockedOut)
{
throw new UserFriendlyException("Cannot proceed because user is locked out!");
From 73de02689f78726665326cb3da2c69c534bb799e Mon Sep 17 00:00:00 2001
From: maliming <6908465+maliming@users.noreply.github.com>
Date: Thu, 9 Jul 2020 10:34:50 +0800
Subject: [PATCH 06/32] Refactoring ISecurityLogManager.
---
.../AspNetCoreSecurityLogManager.cs | 2 +-
.../SecurityLog/DefaultSecurityLogManager.cs | 17 ++--
.../Abp/SecurityLog/ISecurityLogManager.cs | 7 +-
.../Volo/Abp/SecurityLog/SecurityLogInfo.cs | 2 -
.../IdentityServerSupportedLoginModel.cs | 9 +-
.../Account/Controllers/AccountController.cs | 40 +++++----
.../Pages/Account/AccountPageModel.cs | 14 +--
.../Pages/Account/Login.cshtml.cs | 28 +++++-
.../Pages/Account/Logout.cshtml.cs | 7 ++
.../AspNetCore/SignInResultExtensions.cs | 37 ++++++++
.../IdentitySecurityLogActionConsts.cs | 33 +++++++
.../IdentitySecurityLogIdentityConsts.cs | 11 +++
.../Identity/AbpIdentityResultExtensions.cs | 12 ++-
.../Volo/Abp/Identity/SecurityLogEvent.cs | 18 ++++
.../Volo/Abp/Identity/SecurityLogHandler.cs | 85 +++++++++++++++++++
15 files changed, 271 insertions(+), 51 deletions(-)
create mode 100644 modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/SignInResultExtensions.cs
create mode 100644 modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentitySecurityLogActionConsts.cs
create mode 100644 modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentitySecurityLogIdentityConsts.cs
create mode 100644 modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/SecurityLogEvent.cs
create mode 100644 modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/SecurityLogHandler.cs
diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/SecurityLog/AspNetCoreSecurityLogManager.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/SecurityLog/AspNetCoreSecurityLogManager.cs
index 4c60a6ce37..55ece86be2 100644
--- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/SecurityLog/AspNetCoreSecurityLogManager.cs
+++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/SecurityLog/AspNetCoreSecurityLogManager.cs
@@ -49,7 +49,7 @@ namespace Volo.Abp.AspNetCore.SecurityLog
WebClientInfoProvider = webClientInfoProvider;
}
- public override async Task CreateAsync()
+ protected override async Task CreateAsync()
{
var securityLogInfo = await base.CreateAsync();
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/DefaultSecurityLogManager.cs b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/DefaultSecurityLogManager.cs
index 0f5d40567c..fcc03965f2 100644
--- a/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/DefaultSecurityLogManager.cs
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/DefaultSecurityLogManager.cs
@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
@@ -18,17 +19,19 @@ namespace Volo.Abp.SecurityLog
SecurityLogOptions = securityLogOptions.Value;
}
- public virtual Task CreateAsync()
+ public async Task SaveAsync(Action saveAction)
+ {
+ var securityLogInfo = await CreateAsync();
+ saveAction?.Invoke(securityLogInfo);
+ await SecurityLogStore.SaveAsync(securityLogInfo);
+ }
+
+ protected virtual Task CreateAsync()
{
return Task.FromResult(new SecurityLogInfo
{
ApplicationName = SecurityLogOptions.ApplicationName
});
}
-
- public async Task SaveAsync(SecurityLogInfo securityLogInfo)
- {
- await SecurityLogStore.SaveAsync(securityLogInfo);
- }
}
}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogManager.cs b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogManager.cs
index f762522509..0bedfd0411 100644
--- a/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogManager.cs
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/ISecurityLogManager.cs
@@ -1,11 +1,10 @@
-using System.Threading.Tasks;
+using System;
+using System.Threading.Tasks;
namespace Volo.Abp.SecurityLog
{
public interface ISecurityLogManager
{
- Task CreateAsync();
-
- Task SaveAsync(SecurityLogInfo securityLogInfo);
+ Task SaveAsync(Action saveAction);
}
}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/SecurityLogInfo.cs b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/SecurityLogInfo.cs
index 11f46c7527..6185777b97 100644
--- a/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/SecurityLogInfo.cs
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/SecurityLog/SecurityLogInfo.cs
@@ -35,8 +35,6 @@ namespace Volo.Abp.SecurityLog
public string ClientId { get; set; }
- public string ClientName { get; set; }
-
public string CorrelationId { get; set; }
public string ClientIpAddress { get; set; }
diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
index af157ed629..0d90972792 100644
--- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
+++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
@@ -14,6 +14,8 @@ using System.Security.Principal;
using System.Threading.Tasks;
using Volo.Abp.Account.Settings;
using Volo.Abp.DependencyInjection;
+using Volo.Abp.Identity;
+using Volo.Abp.Identity.AspNetCore;
using Volo.Abp.MultiTenancy;
using Volo.Abp.SecurityLog;
using Volo.Abp.Settings;
@@ -128,7 +130,12 @@ namespace Volo.Abp.Account.Web.Pages.Account
true
);
- await CreateSecurityLog("Login_" + result);
+ await LocalEventBus.PublishAsync(new SecurityLogEvent
+ {
+ Identity = IdentitySecurityLogIdentityConsts.Identity,
+ Action = result.ToIdentitySecurityLogAction(),
+ UserName = LoginInput.UserNameOrEmailAddress
+ });
if (result.RequiresTwoFactor)
{
diff --git a/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs b/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs
index 38e8e7e7c2..69b57a42f4 100644
--- a/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs
+++ b/modules/account/src/Volo.Abp.Account.Web/Areas/Account/Controllers/AccountController.cs
@@ -6,7 +6,9 @@ using Volo.Abp.Account.Localization;
using Volo.Abp.Account.Settings;
using Volo.Abp.Account.Web.Areas.Account.Controllers.Models;
using Volo.Abp.AspNetCore.Mvc;
+using Volo.Abp.EventBus.Local;
using Volo.Abp.Identity;
+using Volo.Abp.Identity.AspNetCore;
using Volo.Abp.SecurityLog;
using Volo.Abp.Settings;
using Volo.Abp.Validation;
@@ -26,20 +28,22 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers
protected SignInManager SignInManager { get; }
protected IdentityUserManager UserManager { get; }
protected ISettingProvider SettingProvider { get; }
- protected ISecurityLogManager SecurityLogManager { get; }
+
+ protected ILocalEventBus LocalEventBus { get; }
public AccountController(
SignInManager signInManager,
IdentityUserManager userManager,
ISettingProvider settingProvider,
- ISecurityLogManager securityLogManager)
+ ISecurityLogManager securityLogManager,
+ ILocalEventBus localEventBus)
{
LocalizationResource = typeof(AccountResource);
SignInManager = signInManager;
UserManager = userManager;
SettingProvider = settingProvider;
- SecurityLogManager = securityLogManager;
+ LocalEventBus = localEventBus;
}
[HttpPost]
@@ -51,23 +55,33 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers
ValidateLoginInfo(login);
await ReplaceEmailToUsernameOfInputIfNeeds(login);
- var loginResult = GetAbpLoginResult(await SignInManager.PasswordSignInAsync(
+ var signInResult = await SignInManager.PasswordSignInAsync(
login.UserNameOrEmailAddress,
login.Password,
login.RememberMe,
true
- ));
+ );
- await CreateSecurityLog("Login_" + loginResult.Result);
+ await LocalEventBus.PublishAsync(new SecurityLogEvent
+ {
+ Identity = IdentitySecurityLogIdentityConsts.Identity,
+ Action = signInResult.ToIdentitySecurityLogAction(),
+ UserName = login.UserNameOrEmailAddress
+ });
- return loginResult;
+ return GetAbpLoginResult(signInResult);
}
[HttpGet]
[Route("logout")]
public virtual async Task Logout()
{
- await CreateSecurityLog("Logout");
+ await LocalEventBus.PublishAsync(new SecurityLogEvent
+ {
+ Identity = IdentitySecurityLogIdentityConsts.Identity,
+ Action = IdentitySecurityLogActionConsts.Logout
+ });
+
await SignInManager.SignOutAsync();
}
@@ -133,7 +147,7 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers
return new AbpLoginResult(LoginResultType.InvalidUserNameOrPassword);
}
- return new AbpLoginResult(LoginResultType.Success);
+ return new AbpLoginResult(LoginResultType.Succeeded);
}
protected virtual void ValidateLoginInfo(UserLoginInfo login)
@@ -161,13 +175,5 @@ namespace Volo.Abp.Account.Web.Areas.Account.Controllers
throw new UserFriendlyException(L["LocalLoginDisabledMessage"]);
}
}
-
- protected virtual async Task CreateSecurityLog(string action)
- {
- var securityLog = await SecurityLogManager.CreateAsync();
- securityLog.Identity = "Web";
- securityLog.Action = action;
- await SecurityLogManager.SaveAsync(securityLog);
- }
}
}
diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs
index d7fd0f8239..e645c9c9cf 100644
--- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs
+++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs
@@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.Account.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
+using Volo.Abp.EventBus.Local;
using Volo.Abp.Identity;
-using Volo.Abp.SecurityLog;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
namespace Volo.Abp.Account.Web.Pages.Account
@@ -17,7 +15,7 @@ namespace Volo.Abp.Account.Web.Pages.Account
{
public SignInManager SignInManager { get; set; }
public IdentityUserManager UserManager { get; set; }
- public ISecurityLogManager SecurityLogManager { get; }
+ public ILocalEventBus LocalEventBus { get; set; }
protected AccountPageModel()
{
@@ -79,13 +77,5 @@ namespace Volo.Abp.Account.Web.Pages.Account
{
return "~/"; //TODO: ???
}
-
- protected virtual async Task CreateSecurityLog(string action)
- {
- var securityLog = await SecurityLogManager.CreateAsync();
- securityLog.Identity = "Web";
- securityLog.Action = action;
- await SecurityLogManager.SaveAsync(securityLog);
- }
}
}
diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
index 2916e60b4f..06903ad44f 100644
--- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
+++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
@@ -13,11 +13,12 @@ using System.Threading.Tasks;
using Volo.Abp.Account.Settings;
using Volo.Abp.Auditing;
using Volo.Abp.Identity;
+using Volo.Abp.Identity.AspNetCore;
using Volo.Abp.Security.Claims;
-using Volo.Abp.SecurityLog;
using Volo.Abp.Settings;
using Volo.Abp.Validation;
using IdentityUser = Volo.Abp.Identity.IdentityUser;
+using SignInResult = Microsoft.AspNetCore.Identity.SignInResult;
namespace Volo.Abp.Account.Web.Pages.Account
{
@@ -95,7 +96,12 @@ namespace Volo.Abp.Account.Web.Pages.Account
true
);
- await CreateSecurityLog(result.ToString());
+ await LocalEventBus.PublishAsync(new SecurityLogEvent
+ {
+ Identity = IdentitySecurityLogIdentityConsts.Identity,
+ Action = result.ToIdentitySecurityLogAction(),
+ UserName = LoginInput.UserNameOrEmailAddress
+ });
if (result.RequiresTwoFactor)
{
@@ -184,7 +190,14 @@ namespace Volo.Abp.Account.Web.Pages.Account
bypassTwoFactor: true
);
- await CreateSecurityLog(result.ToString());
+ if (!result.Succeeded)
+ {
+ await LocalEventBus.PublishAsync(new SecurityLogEvent
+ {
+ Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
+ Action = "Login" + result
+ });
+ }
if (result.IsLockedOut)
{
@@ -208,6 +221,15 @@ namespace Volo.Abp.Account.Web.Pages.Account
var user = await CreateExternalUserAsync(info);
await SignInManager.SignInAsync(user, false);
+
+ await LocalEventBus.PublishAsync(new SecurityLogEvent
+ {
+ Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
+ Action = result.ToIdentitySecurityLogAction(),
+ UserName = user.Name,
+ TenantId = user.TenantId
+ });
+
return RedirectSafely(returnUrl, returnUrlHash);
}
diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs
index 8cb2c2bc1e..a9d4ed9a53 100644
--- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs
+++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs
@@ -1,5 +1,6 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
+using Volo.Abp.Identity;
namespace Volo.Abp.Account.Web.Pages.Account
{
@@ -15,6 +16,12 @@ namespace Volo.Abp.Account.Web.Pages.Account
public virtual async Task OnGetAsync()
{
+ await LocalEventBus.PublishAsync(new SecurityLogEvent
+ {
+ Identity = IdentitySecurityLogIdentityConsts.Identity,
+ Action = IdentitySecurityLogActionConsts.Logout
+ });
+
await SignInManager.SignOutAsync();
if (ReturnUrl != null)
{
diff --git a/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/SignInResultExtensions.cs b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/SignInResultExtensions.cs
new file mode 100644
index 0000000000..39e3ae92fc
--- /dev/null
+++ b/modules/identity/src/Volo.Abp.Identity.AspNetCore/Volo/Abp/Identity/AspNetCore/SignInResultExtensions.cs
@@ -0,0 +1,37 @@
+using Microsoft.AspNetCore.Identity;
+
+namespace Volo.Abp.Identity.AspNetCore
+{
+ public static class SignInResultExtensions
+ {
+ public static string ToIdentitySecurityLogAction(this SignInResult result)
+ {
+ if (result.Succeeded)
+ {
+ return IdentitySecurityLogActionConsts.LoginSucceeded;
+ }
+
+ if (result.IsLockedOut)
+ {
+ return IdentitySecurityLogActionConsts.LoginLockedout;
+ }
+
+ if (result.RequiresTwoFactor)
+ {
+ return IdentitySecurityLogActionConsts.LoginRequiresTwoFactor;
+ }
+
+ if (result.IsNotAllowed)
+ {
+ return IdentitySecurityLogActionConsts.LoginNotAllowed;
+ }
+
+ if (!result.Succeeded)
+ {
+ return IdentitySecurityLogActionConsts.LoginFailed;
+ }
+
+ return IdentitySecurityLogActionConsts.LoginFailed;
+ }
+ }
+}
diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentitySecurityLogActionConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentitySecurityLogActionConsts.cs
new file mode 100644
index 0000000000..ea5276e673
--- /dev/null
+++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentitySecurityLogActionConsts.cs
@@ -0,0 +1,33 @@
+namespace Volo.Abp.Identity
+{
+ public class IdentitySecurityLogActionConsts
+ {
+ public static string LoginSucceeded { get; set; } = "LoginSucceeded";
+
+ public static string LoginLockedout { get; set; } = "LoginLockedout";
+
+ public static string LoginNotAllowed { get; set; } = "LoginNotAllowed";
+
+ public static string LoginRequiresTwoFactor { get; set; } = "LoginRequiresTwoFactor";
+
+ public static string LoginFailed { get; set; } = "LoginFailed";
+
+ public static string LoginInvalidUserName { get; set; } = "LoginInvalidUserName";
+
+ public static string LoginInvalidUserNameOrPassword { get; set; } = "LoginInvalidUserNameOrPassword";
+
+ public static string Logout { get; set; } = "Logout";
+
+ public static string ChangeUserName { get; set; } = "ChangeUserName";
+
+ public static string ChangeEmail { get; set; } = "ChangeEmail";
+
+ public static string ChangePhoneNumber { get; set; } = "ChangePhoneNumber";
+
+ public static string ChangePassword { get; set; } = "ChangePassword";
+
+ public static string TwoFactorEnabled { get; set; } = "TwoFactorEnabled";
+
+ public static string TwoFactorDisabled { get; set; } = "TwoFactorDisabled";
+ }
+}
diff --git a/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentitySecurityLogIdentityConsts.cs b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentitySecurityLogIdentityConsts.cs
new file mode 100644
index 0000000000..5616dfb4e6
--- /dev/null
+++ b/modules/identity/src/Volo.Abp.Identity.Domain.Shared/Volo/Abp/Identity/IdentitySecurityLogIdentityConsts.cs
@@ -0,0 +1,11 @@
+namespace Volo.Abp.Identity
+{
+ public static class IdentitySecurityLogIdentityConsts
+ {
+ public static string Identity { get; set; } = "Identity";
+
+ public static string IdentityExternal { get; set; } = "IdentityExternal";
+
+ public static string IdentityTwoFactor { get; set; } = "IdentityTwoFactor";
+ }
+}
diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs
index 58349e8b69..0ef3582af1 100644
--- a/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs
+++ b/modules/identity/src/Volo.Abp.Identity.Domain/Microsoft/AspNetCore/Identity/AbpIdentityResultExtensions.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Globalization;
using Microsoft.Extensions.Localization;
using Volo.Abp.Identity;
+using Volo.Abp.Localization;
using Volo.Abp.Text.Formatting;
namespace Microsoft.AspNetCore.Identity
@@ -48,12 +49,15 @@ namespace Microsoft.AspNetCore.Identity
if (!localizedString.ResourceNotFound)
{
- var englishLocalizedString = localizer.WithCulture(CultureInfo.GetCultureInfo("en"))[key];
- if (!englishLocalizedString.ResourceNotFound)
+ using (CultureHelper.Use(CultureInfo.GetCultureInfo("en")))
{
- if (FormattedStringValueExtracter.IsMatch(error.Description, englishLocalizedString.Value, out var values))
+ var englishLocalizedString = localizer[key];
+ if (!englishLocalizedString.ResourceNotFound)
{
- return string.Format(localizedString.Value, values.Cast