mirror of https://github.com/abpframework/abp.git
113 changed files with 2459 additions and 403 deletions
@ -0,0 +1,66 @@ |
|||||
|
# BLOB Storing Minio Provider |
||||
|
|
||||
|
BLOB Storing Minio Provider can store BLOBs in [MinIO Object storage](https://min.io/). |
||||
|
|
||||
|
> Read the [BLOB Storing document](Blob-Storing.md) to understand how to use the BLOB storing system. This document only covers how to configure containers to use a Minio BLOB as the storage provider. |
||||
|
|
||||
|
## Installation |
||||
|
|
||||
|
Use the ABP CLI to add [Volo.Abp.BlobStoring.Minio](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Minio) NuGet package to your project: |
||||
|
|
||||
|
* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before. |
||||
|
* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.BlobStoring.Minio` package. |
||||
|
* Run `abp add-package Volo.Abp.BlobStoring.Minio` command. |
||||
|
|
||||
|
If you want to do it manually, install the [Volo.Abp.BlobStoring.Minio](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Minio) NuGet package to your project and add `[DependsOn(typeof(AbpBlobStoringMinioModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project. |
||||
|
|
||||
|
## Configuration |
||||
|
|
||||
|
Configuration is done in the `ConfigureServices` method of your [module](Module-Development-Basics.md) class, as explained in the [BLOB Storing document](Blob-Storing.md). |
||||
|
|
||||
|
**Example: Configure to use the minio storage provider by default** |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpBlobStoringOptions>(options => |
||||
|
{ |
||||
|
options.Containerscontainer.UseMinio(minio => |
||||
|
{ |
||||
|
minio.EndPoint = "your minio endPoint"; |
||||
|
minio.AccessKey = "your minio accessKey"; |
||||
|
minio.SecretKey = "your minio secretKey"; |
||||
|
minio.BucketName = "your minio bucketName"; |
||||
|
}); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
> See the [BLOB Storing document](Blob-Storing.md) to learn how to configure this provider for a specific container. |
||||
|
|
||||
|
### Options |
||||
|
|
||||
|
* **EndPoint** (string): URL to object storage service. Please refer to MinIO Client SDK for .NET: https://docs.min.io/docs/dotnet-client-quickstart-guide.html |
||||
|
* **AccessKey** (string): Access key is the user ID that uniquely identifies your account. |
||||
|
* **SecretKey** (string): Secret key is the password to your account. |
||||
|
* **BucketName** (string): You can specify the bucket name in MinIO. If this is not specified, it uses the name of the BLOB container defined with the `BlogContainerName` attribute (see the [BLOB storing document](Blob-Storing.md)).MinIO is the defacto standard for S3 compatibility, So MinIO has some **rules for naming bucket**. The [following rules](https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html) apply for naming MinIO buckets: |
||||
|
* Bucket names must be between **3** and **63** characters long. |
||||
|
* Bucket names can consist only of **lowercase** letters, numbers, dots (.), and hyphens (-). |
||||
|
* Bucket names must begin and end with a letter or number. |
||||
|
* Bucket names must not be formatted as an IP address (for example, 192.168.5.4). |
||||
|
* Bucket names can't begin with **xn--** (for buckets created after February 2020). |
||||
|
* Bucket names must be unique within a partition. |
||||
|
* Buckets used with Amazon S3 Transfer Acceleration can't have dots (.) in their names. For more information about transfer acceleration, see Amazon S3 Transfer Acceleration. |
||||
|
* **WithSSL** (bool): Default value is `false`,Chain to MinIO Client object to use https instead of http. |
||||
|
* **CreateContainerIfNotExists** (bool): Default value is `false`, If a bucket does not exist in minio, `MinioBlobProvider` will try to create it. |
||||
|
|
||||
|
|
||||
|
## Minio Blob Name Calculator |
||||
|
|
||||
|
Minio Blob Provider organizes BLOB name and implements some conventions. The full name of a BLOB is determined by the following rules by default: |
||||
|
|
||||
|
* Appends `host` string if [current tenant](Multi-Tenancy.md) is `null` (or multi-tenancy is disabled for the container - see the [BLOB Storing document](Blob-Storing.md) to learn how to disable multi-tenancy for a container). |
||||
|
* Appends `tenants/<tenant-id>` string if current tenant is not `null`. |
||||
|
* Appends the BLOB name. |
||||
|
|
||||
|
## Other Services |
||||
|
|
||||
|
* `MinioBlobProvider` is the main service that implements the Minio BLOB storage provider, if you want to override/replace it via [dependency injection](Dependency-Injection.md) (don't replace `IBlobProvider` interface, but replace `MinioBlobProvider` class). |
||||
|
* `IMinioBlobNameCalculator` is used to calculate the full BLOB name (that is explained above). It is implemented by the `DefaultMinioBlobNameCalculator` by default. |
||||
@ -0,0 +1,66 @@ |
|||||
|
# BLOB Storing Minio 提供程序 |
||||
|
|
||||
|
BLOB Storing Minio提供程序帮助你存储对象到 [MinIO Object storage](https://min.io/), |
||||
|
|
||||
|
> 阅读[BLOB存储文档](Blob-Storing.md)了解如何使用BLOB存储系统, 本文档仅介绍如何为容器配置Minio提供程序, |
||||
|
|
||||
|
## 安装 |
||||
|
|
||||
|
使用 ABP CLI 来安装 [Volo.Abp.BlobStoring.Minio](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Minio) NuGet 包到你的项目: |
||||
|
|
||||
|
* 如果你没有安装ABP CLI,请先安装 [ABP CLI](https://docs.abp.io/en/abp/latest/CLI), |
||||
|
* 在要添加 `Volo.Abp.BlobStoring.Minio` 包的 `.csproj` 文件目录打开命令行, |
||||
|
* 执行 `abp add-package Volo.Abp.BlobStoring.Minio` 命令, |
||||
|
|
||||
|
如果你要手动安装, 通过NuGet安装 [Volo.Abp.BlobStoring.Minio](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Minio) 到你的项目,然后添加 `[DependsOn(typeof(AbpBlobStoringMinioModule))]` 特性到你的 [ABP module](Module-Development-Basics.md) 类上, |
||||
|
|
||||
|
## 配置 |
||||
|
|
||||
|
配置在你的[module](Module-Development-Basics.md)类中的`ConfigureServices`方法中完成, |
||||
|
|
||||
|
**例: 配置使用Minio存储** |
||||
|
|
||||
|
````csharp |
||||
|
Configure<AbpBlobStoringOptions>(options => |
||||
|
{ |
||||
|
options.Containerscontainer.UseMinio(minio => |
||||
|
{ |
||||
|
minio.EndPoint = "你的 minio endPoint"; |
||||
|
minio.AccessKey = "你的 minio accessKey"; |
||||
|
minio.SecretKey = "你的 minio secretKey"; |
||||
|
minio.BucketName = "你的 minio bucketName"; |
||||
|
}); |
||||
|
}); |
||||
|
```` |
||||
|
|
||||
|
> 参阅[BLOB存储文档](Blob-Storing.md) 学习如何为指定容器配置提供程序, |
||||
|
|
||||
|
### 选项 |
||||
|
|
||||
|
* **EndPoint** (string): 你的Minio对象存储服务的URL, 查看文档:https://docs.min.io/docs/dotnet-client-quickstart-guide.html |
||||
|
* **AccessKey** (string): Access key是唯一标识你的账户的用户ID, |
||||
|
* **SecretKey** (string): Access key是唯一标识你的账户的用户ID |
||||
|
* **BucketName** (string):你可以指定bucket名称,如果没有指定,将使用 `BlogContainerName` 属性定义的BLOB容器的名称(查阅[BLOB storing document](Blob-Storing.md)),MinIO完全兼容S3标准,所以有一些 **bucket命名规则**,必须符合[规则](https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html): |
||||
|
* Bucket名字必须 **3** 到 **63** 字符长度. |
||||
|
* Bucket名字必须是 **小写** 的字母,数字,点号(.), 横杠 (-), |
||||
|
* Bucket名字必须是以数字和字母开头和结尾, |
||||
|
* Bucket名字不能是ip (例如, 192.168.5.4), |
||||
|
* Bucket名字不能以 **xn--** 开头, (2020年2月以后创建), |
||||
|
* Bucket名字必须区块唯一 |
||||
|
* Buckets如果使用Amazon S3加速传输名字不能有点号(.), |
||||
|
* **WithSSL** (bool): 默认 `false`,代表使用HTTPS, |
||||
|
* **CreateContainerIfNotExists** (bool): 默认 `false`,如果不存在bucket, `MinioBlobProvider` 将会创建一个, |
||||
|
|
||||
|
|
||||
|
## Minio BLOB 名称计算器 |
||||
|
|
||||
|
默认情况下BLOB的全名由以下规则确定: |
||||
|
|
||||
|
* 如果当前租户为 `null`(或容器禁用多租户 - 请参阅[BLOB存储文档](Blob-Storing.md) 了解如何禁用容器的多租户),则追加 `host` 字符串, |
||||
|
* 如果当前租户不为 `null`,则追加 `tenants/<tenant-id>` 字符串, |
||||
|
* 追加 BLOB 名称, |
||||
|
|
||||
|
## 其他服务 |
||||
|
|
||||
|
* `MinioBlobProvider` 是实现Minio BLOB存储提供程序的主要服务,如果你想要通过[依赖注入](Dependency-Injection.md)覆盖/替换它(不要替换 `IBlobProvider` 接口,而是替换 `MinioBlobProvider` 类). |
||||
|
* `IMinioBlobNameCalculator` 服务用于计算文件路径. 默认实现是 `DefaultMinioBlobNameCalculator`. 如果你想自定义文件路径计算,可以替换/覆盖它. |
||||
@ -0,0 +1,73 @@ |
|||||
|
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<AspNetCoreSecurityLogManager> 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<AbpSecurityLogOptions> securityLogOptions, |
||||
|
ISecurityLogStore securityLogStore, |
||||
|
ILogger<AspNetCoreSecurityLogManager> 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; |
||||
|
} |
||||
|
|
||||
|
protected override async Task<SecurityLogInfo> 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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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<HttpContextWebClientInfoProvider> Logger { get; } |
||||
|
protected IHttpContextAccessor HttpContextAccessor { get; } |
||||
|
|
||||
|
public HttpContextWebClientInfoProvider( |
||||
|
ILogger<HttpContextWebClientInfoProvider> 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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
namespace Volo.Abp.AspNetCore.WebClientInfo |
||||
|
{ |
||||
|
public interface IWebClientInfoProvider |
||||
|
{ |
||||
|
string BrowserInfo { get; } |
||||
|
|
||||
|
string ClientIpAddress { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.EventBus |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// An event data object (or event transfer object) can implement this interface
|
||||
|
/// to indicate that this event may be related to a tenant.
|
||||
|
///
|
||||
|
/// If an event data class is always related to a tenant, then directly implement the
|
||||
|
/// <see cref="IsMultiTenant"/> interface instead of this one.
|
||||
|
///
|
||||
|
/// This interface is typically implemented by generic event handlers where the generic
|
||||
|
/// parameter may implement <see cref="IsMultiTenant"/> or not.
|
||||
|
/// </summary>
|
||||
|
public interface IEventDataMayHaveTenantId |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Returns true if this event data has a Tenant Id information.
|
||||
|
/// If so, it should set the <see cref="tenantId"/> our parameter.
|
||||
|
/// Otherwise, the <see cref="tenantId"/> our parameter value should not be informative
|
||||
|
/// (it will be null as expected, but doesn't indicate a tenant with null tenant id).
|
||||
|
/// </summary>
|
||||
|
/// <param name="tenantId">
|
||||
|
/// The tenant id that is set if this method returns true.
|
||||
|
/// </param>
|
||||
|
bool IsMultiTenant(out Guid? tenantId); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
namespace Volo.Abp.SecurityLog |
||||
|
{ |
||||
|
public class AbpSecurityLogOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Default: true.
|
||||
|
/// </summary>
|
||||
|
public bool IsEnabled { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The name of the application or service writing security log.
|
||||
|
/// Default: null.
|
||||
|
/// </summary>
|
||||
|
public string ApplicationName { get; set; } |
||||
|
|
||||
|
public AbpSecurityLogOptions() |
||||
|
{ |
||||
|
IsEnabled = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
using System; |
||||
|
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<AbpSecurityLogOptions> securityLogOptions, |
||||
|
ISecurityLogStore securityLogStore) |
||||
|
{ |
||||
|
SecurityLogStore = securityLogStore; |
||||
|
SecurityLogOptions = securityLogOptions.Value; |
||||
|
} |
||||
|
|
||||
|
public async Task SaveAsync(Action<SecurityLogInfo> saveAction = null) |
||||
|
{ |
||||
|
if (!SecurityLogOptions.IsEnabled) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var securityLogInfo = await CreateAsync(); |
||||
|
saveAction?.Invoke(securityLogInfo); |
||||
|
await SecurityLogStore.SaveAsync(securityLogInfo); |
||||
|
} |
||||
|
|
||||
|
protected virtual Task<SecurityLogInfo> CreateAsync() |
||||
|
{ |
||||
|
return Task.FromResult(new SecurityLogInfo |
||||
|
{ |
||||
|
ApplicationName = SecurityLogOptions.ApplicationName |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.SecurityLog |
||||
|
{ |
||||
|
public interface ISecurityLogManager |
||||
|
{ |
||||
|
Task SaveAsync(Action<SecurityLogInfo> saveAction = null); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.SecurityLog |
||||
|
{ |
||||
|
public interface ISecurityLogStore |
||||
|
{ |
||||
|
Task SaveAsync(SecurityLogInfo securityLogInfo); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.SecurityLog |
||||
|
{ |
||||
|
[Serializable] |
||||
|
public class SecurityLogInfo |
||||
|
{ |
||||
|
public string ApplicationName { get; set; } |
||||
|
|
||||
|
public string Identity { get; set; } |
||||
|
|
||||
|
public string Action { get; set; } |
||||
|
|
||||
|
public Dictionary<string, object> 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 CorrelationId { get; set; } |
||||
|
|
||||
|
public string ClientIpAddress { get; set; } |
||||
|
|
||||
|
public string BrowserInfo { get; set; } |
||||
|
|
||||
|
public DateTime CreationTime { get; set; } |
||||
|
|
||||
|
public SecurityLogInfo() |
||||
|
{ |
||||
|
ExtraProperties = new Dictionary<string, object>(); |
||||
|
} |
||||
|
|
||||
|
public override string ToString() |
||||
|
{ |
||||
|
return $"SECURITY LOG: [{ApplicationName} - {Identity} - {Action}]"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.SecurityLog |
||||
|
{ |
||||
|
public class SimpleSecurityLogStore : ISecurityLogStore, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<SimpleSecurityLogStore> Logger { get; set; } |
||||
|
protected AbpSecurityLogOptions SecurityLogOptions { get; } |
||||
|
|
||||
|
public SimpleSecurityLogStore(ILogger<SimpleSecurityLogStore> logger, IOptions<AbpSecurityLogOptions> securityLogOptions) |
||||
|
{ |
||||
|
Logger = logger; |
||||
|
SecurityLogOptions = securityLogOptions.Value; |
||||
|
} |
||||
|
|
||||
|
public Task SaveAsync(SecurityLogInfo securityLogInfo) |
||||
|
{ |
||||
|
if (!SecurityLogOptions.IsEnabled) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
Logger.LogInformation(securityLogInfo.ToString()); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using NSubstitute; |
||||
|
using Volo.Abp.SecurityLog; |
||||
|
using Volo.Abp.Testing; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Security.SecurityLog |
||||
|
{ |
||||
|
|
||||
|
public class SecurityLogManager_Tests : AbpIntegratedTest<AbpSecurityTestModule> |
||||
|
{ |
||||
|
private readonly ISecurityLogManager _securityLogManager; |
||||
|
|
||||
|
private ISecurityLogStore _auditingStore; |
||||
|
|
||||
|
public SecurityLogManager_Tests() |
||||
|
{ |
||||
|
_securityLogManager = GetRequiredService<ISecurityLogManager>(); |
||||
|
} |
||||
|
|
||||
|
protected override void AfterAddApplication(IServiceCollection services) |
||||
|
{ |
||||
|
_auditingStore = Substitute.For<ISecurityLogStore>(); |
||||
|
services.AddSingleton(_auditingStore); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task SaveAsync() |
||||
|
{ |
||||
|
await _securityLogManager.SaveAsync(securityLog => |
||||
|
{ |
||||
|
securityLog.Identity = "Test"; |
||||
|
securityLog.Action = "Test-Action"; |
||||
|
securityLog.UserName = "Test-User"; |
||||
|
}); |
||||
|
|
||||
|
await _auditingStore.Received().SaveAsync(Arg.Is<SecurityLogInfo>(log => |
||||
|
log.ApplicationName == "AbpSecurityTest" && |
||||
|
log.Identity == "Test" && |
||||
|
log.Action == "Test-Action" && |
||||
|
log.UserName == "Test-User")); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,25 +1,46 @@ |
|||||
(function () { |
(function ($) { |
||||
$(document).ready(function () { |
$(document).ready(function () { |
||||
|
|
||||
function initReactionSelection() { |
abp.widgets.CmsReactionSelection = function ($widget) { |
||||
var $wrapper = $(this); |
|
||||
var $availableReactions = $wrapper.find('.cms-reaction-selection-available-reactions'); |
|
||||
|
|
||||
$wrapper.find('.cms-reaction-icon').each(function () { |
function getFilters() { |
||||
var $icon = $(this); |
return {}; |
||||
$icon.click(function () { |
} |
||||
var methodName = $icon.hasClass('cms-reaction-icon-selected') ? 'delete' : 'create'; |
|
||||
volo.cmsKit.reactions.reactionPublic[methodName]({ |
function refresh(filters) { |
||||
entityType: $wrapper.attr('data-entity-type'), |
location.reload(); //TODO: JUST TESTING !!!!!!!!
|
||||
entityId: $wrapper.attr('data-entity-id'), |
} |
||||
reactionName: $icon.attr('data-name') |
|
||||
}).then(function () { |
function init(filters) { |
||||
location.reload(); //TODO: JUST TESTING !!!!!!!!
|
var $wrapper = $widget.find('.cms-reaction-selection'); |
||||
|
$widget.find('.cms-reaction-icon').each(function () { |
||||
|
var $icon = $(this); |
||||
|
$icon.click(function () { |
||||
|
var methodName = $icon.hasClass('cms-reaction-icon-selected') ? 'delete' : 'create'; |
||||
|
volo.cmsKit.reactions.reactionPublic[methodName]({ |
||||
|
entityType: $wrapper.attr('data-entity-type'), |
||||
|
entityId: $wrapper.attr('data-entity-id'), |
||||
|
reactionName: $icon.attr('data-name') |
||||
|
}).then(function () { |
||||
|
refresh(); |
||||
|
}); |
||||
}); |
}); |
||||
}); |
}); |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
init: init, |
||||
|
refresh: refresh, |
||||
|
getFilters : getFilters |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
$('.abp-widget-wrapper[data-widget-name="CmsReactionSelection"]').each(function () { |
||||
|
var widgetManager = new abp.WidgetManager({ |
||||
|
wrapper: $(this), |
||||
}); |
}); |
||||
} |
|
||||
|
|
||||
$('.cms-reaction-selection').each(initReactionSelection); |
widgetManager.init(); |
||||
|
}); |
||||
}); |
}); |
||||
})(); |
})(jQuery); |
||||
|
|||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
namespace Volo.Abp.Identity |
||||
|
{ |
||||
|
public class IdentitySecurityLogConsts |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Default value: 96
|
||||
|
/// </summary>
|
||||
|
public static int MaxApplicationNameLength { get; set; } = 96; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default value: 96
|
||||
|
/// </summary>
|
||||
|
public static int MaxIdentityLength { get; set; } = 96; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default value: 96
|
||||
|
/// </summary>
|
||||
|
public static int MaxActionLength { get; set; } = 96; |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default value: 256
|
||||
|
/// </summary>
|
||||
|
public static int MaxUserNameLength { get; set; } = 256; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default value: 64
|
||||
|
/// </summary>
|
||||
|
public static int MaxTenantNameLength { get; set; } = 64; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default value: 64
|
||||
|
/// </summary>
|
||||
|
public static int MaxClientIpAddressLength { get; set; } = 64; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default value: 64
|
||||
|
/// </summary>
|
||||
|
public static int MaxClientIdLength { get; set; } = 64; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default value: 64
|
||||
|
/// </summary>
|
||||
|
public static int MaxCorrelationIdLength { get; set; } = 64; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default value: 512
|
||||
|
/// </summary>
|
||||
|
public static int MaxBrowserInfoLength { get; set; } = 512; |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -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"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Domain.Repositories; |
||||
|
|
||||
|
namespace Volo.Abp.Identity |
||||
|
{ |
||||
|
public interface IIdentitySecurityLogRepository : IBasicRepository<IdentitySecurityLog, Guid> |
||||
|
{ |
||||
|
Task<List<IdentitySecurityLog>> GetListAsync( |
||||
|
string sorting = null, |
||||
|
int maxResultCount = 50, |
||||
|
int skipCount = 0, |
||||
|
DateTime? startTime = null, |
||||
|
DateTime? endTime = null, |
||||
|
string applicationName = null, |
||||
|
string identity = null, |
||||
|
string action = null, |
||||
|
Guid? userId = null, |
||||
|
string userName = null, |
||||
|
string clientId = null, |
||||
|
string correlationId = null, |
||||
|
bool includeDetails = false, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
|
||||
|
Task<long> GetCountAsync( |
||||
|
DateTime? startTime = null, |
||||
|
DateTime? endTime = null, |
||||
|
string applicationName = null, |
||||
|
string identity = null, |
||||
|
string action = null, |
||||
|
Guid? userId = null, |
||||
|
string userName = null, |
||||
|
string clientId = null, |
||||
|
string correlationId = null, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
|
||||
|
Task<IdentitySecurityLog> GetByUserIdAsync( |
||||
|
Guid id, |
||||
|
Guid userId, |
||||
|
bool includeDetails = false, |
||||
|
CancellationToken cancellationToken = default); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,64 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Domain.Entities; |
||||
|
using Volo.Abp.Guids; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.SecurityLog; |
||||
|
|
||||
|
namespace Volo.Abp.Identity |
||||
|
{ |
||||
|
public class IdentitySecurityLog : AggregateRoot<Guid>, IMultiTenant |
||||
|
{ |
||||
|
public Guid? TenantId { get; protected set; } |
||||
|
|
||||
|
public string ApplicationName { get; protected set; } |
||||
|
|
||||
|
public string Identity { get; protected set; } |
||||
|
|
||||
|
public string Action { get; protected set; } |
||||
|
|
||||
|
public Guid? UserId { get; protected set; } |
||||
|
|
||||
|
public string UserName { get; protected set; } |
||||
|
|
||||
|
public string TenantName { get; protected set; } |
||||
|
|
||||
|
public string ClientId { get; protected set; } |
||||
|
|
||||
|
public string CorrelationId { get; protected set; } |
||||
|
|
||||
|
public string ClientIpAddress { get; protected set; } |
||||
|
|
||||
|
public string BrowserInfo { get; protected set; } |
||||
|
|
||||
|
public DateTime CreationTime { get; protected set; } |
||||
|
|
||||
|
protected IdentitySecurityLog() |
||||
|
{ |
||||
|
ExtraProperties = new Dictionary<string, object>(); |
||||
|
} |
||||
|
|
||||
|
public IdentitySecurityLog(IGuidGenerator guidGenerator, SecurityLogInfo securityLogInfo) |
||||
|
{ |
||||
|
Id = guidGenerator.Create(); |
||||
|
TenantId = securityLogInfo.TenantId; |
||||
|
TenantName = securityLogInfo.TenantName.Truncate(IdentitySecurityLogConsts.MaxTenantNameLength); |
||||
|
|
||||
|
ApplicationName = securityLogInfo.ApplicationName.Truncate(IdentitySecurityLogConsts.MaxApplicationNameLength); |
||||
|
Identity = securityLogInfo.Identity.Truncate(IdentitySecurityLogConsts.MaxIdentityLength); |
||||
|
Action = securityLogInfo.Action.Truncate(IdentitySecurityLogConsts.MaxActionLength); |
||||
|
|
||||
|
UserId = securityLogInfo.UserId; |
||||
|
UserName = securityLogInfo.UserName.Truncate(IdentitySecurityLogConsts.MaxUserNameLength); |
||||
|
|
||||
|
CreationTime = securityLogInfo.CreationTime; |
||||
|
|
||||
|
ClientIpAddress = securityLogInfo.ClientIpAddress.Truncate(IdentitySecurityLogConsts.MaxClientIpAddressLength); |
||||
|
ClientId = securityLogInfo.ClientId.Truncate(IdentitySecurityLogConsts.MaxClientIdLength); |
||||
|
CorrelationId = securityLogInfo.CorrelationId.Truncate(IdentitySecurityLogConsts.MaxCorrelationIdLength); |
||||
|
BrowserInfo = securityLogInfo.BrowserInfo.Truncate(IdentitySecurityLogConsts.MaxBrowserInfoLength); |
||||
|
|
||||
|
ExtraProperties = securityLogInfo.ExtraProperties; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
|
||||
|
namespace Volo.Abp.Identity |
||||
|
{ |
||||
|
public class IdentitySecurityLogEvent : IMultiTenant |
||||
|
{ |
||||
|
public Guid? TenantId { get; set; } |
||||
|
|
||||
|
public string Identity { get; set; } |
||||
|
|
||||
|
public string Action { get; set; } |
||||
|
|
||||
|
public string UserName { get; set; } |
||||
|
|
||||
|
public string ClientId { get; set; } |
||||
|
|
||||
|
public Dictionary<string, object> ExtraProperties { get; } |
||||
|
|
||||
|
public IdentitySecurityLogEvent() |
||||
|
{ |
||||
|
ExtraProperties = new Dictionary<string, object>(); |
||||
|
} |
||||
|
|
||||
|
public virtual IdentitySecurityLogEvent WithProperty(string key, object value) |
||||
|
{ |
||||
|
ExtraProperties[key] = value; |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,86 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Identity; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.EventBus; |
||||
|
using Volo.Abp.Security.Claims; |
||||
|
using Volo.Abp.SecurityLog; |
||||
|
using Volo.Abp.Uow; |
||||
|
using Volo.Abp.Users; |
||||
|
|
||||
|
namespace Volo.Abp.Identity |
||||
|
{ |
||||
|
public class IdentitySecurityLogHandler : ILocalEventHandler<IdentitySecurityLogEvent>, ITransientDependency |
||||
|
{ |
||||
|
protected ISecurityLogManager SecurityLogManager { get; } |
||||
|
protected IdentityUserManager UserManager { get; } |
||||
|
protected ICurrentPrincipalAccessor CurrentPrincipalAccessor { get; } |
||||
|
protected IUserClaimsPrincipalFactory<IdentityUser> UserClaimsPrincipalFactory { get; } |
||||
|
protected ICurrentUser CurrentUser { get; } |
||||
|
|
||||
|
public IdentitySecurityLogHandler( |
||||
|
ISecurityLogManager securityLogManager, |
||||
|
IdentityUserManager userManager, |
||||
|
ICurrentPrincipalAccessor currentPrincipalAccessor, |
||||
|
IUserClaimsPrincipalFactory<IdentityUser> userClaimsPrincipalFactory, |
||||
|
ICurrentUser currentUser) |
||||
|
{ |
||||
|
SecurityLogManager = securityLogManager; |
||||
|
UserManager = userManager; |
||||
|
CurrentPrincipalAccessor = currentPrincipalAccessor; |
||||
|
UserClaimsPrincipalFactory = userClaimsPrincipalFactory; |
||||
|
CurrentUser = currentUser; |
||||
|
} |
||||
|
|
||||
|
public async Task HandleEventAsync(IdentitySecurityLogEvent eventData) |
||||
|
{ |
||||
|
Action<SecurityLogInfo> securityLogAction = securityLog => |
||||
|
{ |
||||
|
securityLog.Identity = eventData.Identity; |
||||
|
securityLog.Action = eventData.Action; |
||||
|
|
||||
|
if (securityLog.UserName.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
securityLog.UserName = eventData.UserName; |
||||
|
} |
||||
|
|
||||
|
if (securityLog.ClientId.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
securityLog.ClientId = eventData.ClientId; |
||||
|
} |
||||
|
|
||||
|
foreach (var property in eventData.ExtraProperties) |
||||
|
{ |
||||
|
securityLog.ExtraProperties[property.Key] = property.Value; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
if (CurrentUser.IsAuthenticated) |
||||
|
{ |
||||
|
await SecurityLogManager.SaveAsync(securityLogAction); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (eventData.UserName.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
await SecurityLogManager.SaveAsync(securityLogAction); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var user = await UserManager.FindByNameAsync(eventData.UserName); |
||||
|
if (user != null) |
||||
|
{ |
||||
|
using (CurrentPrincipalAccessor.Change(await UserClaimsPrincipalFactory.CreateAsync(user))) |
||||
|
{ |
||||
|
await SecurityLogManager.SaveAsync(securityLogAction); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
await SecurityLogManager.SaveAsync(securityLogAction); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Guids; |
||||
|
using Volo.Abp.SecurityLog; |
||||
|
using Volo.Abp.Uow; |
||||
|
|
||||
|
namespace Volo.Abp.Identity |
||||
|
{ |
||||
|
[Dependency(ReplaceServices = true)] |
||||
|
public class IdentitySecurityLogStore : ISecurityLogStore, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<IdentitySecurityLogStore> Logger { get; set; } |
||||
|
|
||||
|
protected AbpSecurityLogOptions SecurityLogOptions { get; } |
||||
|
protected IIdentitySecurityLogRepository IdentitySecurityLogRepository { get; } |
||||
|
protected IGuidGenerator GuidGenerator { get; } |
||||
|
protected IUnitOfWorkManager UnitOfWorkManager { get; } |
||||
|
|
||||
|
public IdentitySecurityLogStore( |
||||
|
ILogger<IdentitySecurityLogStore> logger, |
||||
|
IOptions<AbpSecurityLogOptions> securityLogOptions, |
||||
|
IIdentitySecurityLogRepository identitySecurityLogRepository, |
||||
|
IGuidGenerator guidGenerator, |
||||
|
IUnitOfWorkManager unitOfWorkManager) |
||||
|
{ |
||||
|
Logger = logger; |
||||
|
SecurityLogOptions = securityLogOptions.Value; |
||||
|
IdentitySecurityLogRepository = identitySecurityLogRepository; |
||||
|
GuidGenerator = guidGenerator; |
||||
|
UnitOfWorkManager = unitOfWorkManager; |
||||
|
} |
||||
|
|
||||
|
public async Task SaveAsync(SecurityLogInfo securityLogInfo) |
||||
|
{ |
||||
|
if (!SecurityLogOptions.IsEnabled) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
using (var uow = UnitOfWorkManager.Begin(requiresNew: true)) |
||||
|
{ |
||||
|
await IdentitySecurityLogRepository.InsertAsync(new IdentitySecurityLog(GuidGenerator, securityLogInfo)); |
||||
|
await uow.CompleteAsync(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,109 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Linq.Dynamic.Core; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
|
||||
|
namespace Volo.Abp.Identity.EntityFrameworkCore |
||||
|
{ |
||||
|
public class EFCoreIdentitySecurityLogRepository : EfCoreRepository<IIdentityDbContext, IdentitySecurityLog, Guid>, IIdentitySecurityLogRepository |
||||
|
{ |
||||
|
public EFCoreIdentitySecurityLogRepository(IDbContextProvider<IIdentityDbContext> dbContextProvider) |
||||
|
: base(dbContextProvider) |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public async Task<List<IdentitySecurityLog>> GetListAsync( |
||||
|
string sorting = null, |
||||
|
int maxResultCount = 50, |
||||
|
int skipCount = 0, |
||||
|
DateTime? startTime = null, |
||||
|
DateTime? endTime = null, |
||||
|
string applicationName = null, |
||||
|
string identity = null, |
||||
|
string action = null, |
||||
|
Guid? userId = null, |
||||
|
string userName = null, |
||||
|
string clientId = null, |
||||
|
string correlationId = null, |
||||
|
bool includeDetails = false, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var query = GetListQuery( |
||||
|
startTime, |
||||
|
endTime, |
||||
|
applicationName, |
||||
|
identity, |
||||
|
action, |
||||
|
userId, |
||||
|
userName, |
||||
|
clientId, |
||||
|
correlationId |
||||
|
); |
||||
|
|
||||
|
return await query.OrderBy(sorting ?? nameof(IdentitySecurityLog.CreationTime) + " desc") |
||||
|
.PageBy(skipCount, maxResultCount) |
||||
|
.ToListAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
public async Task<long> GetCountAsync( |
||||
|
DateTime? startTime = null, |
||||
|
DateTime? endTime = null, |
||||
|
string applicationName = null, |
||||
|
string identity = null, |
||||
|
string action = null, |
||||
|
Guid? userId = null, |
||||
|
string userName = null, |
||||
|
string clientId = null, |
||||
|
string correlationId = null, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var query = GetListQuery( |
||||
|
startTime, |
||||
|
endTime, |
||||
|
applicationName, |
||||
|
identity, |
||||
|
action, |
||||
|
userId, |
||||
|
userName, |
||||
|
clientId, |
||||
|
correlationId |
||||
|
); |
||||
|
|
||||
|
return await query.LongCountAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
public async Task<IdentitySecurityLog> GetByUserIdAsync(Guid id, Guid userId, bool includeDetails = false, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await DbSet.FirstOrDefaultAsync(x => x.Id == id && x.UserId == userId, GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
protected virtual IQueryable<IdentitySecurityLog> GetListQuery( |
||||
|
DateTime? startTime = null, |
||||
|
DateTime? endTime = null, |
||||
|
string applicationName = null, |
||||
|
string identity = null, |
||||
|
string action = null, |
||||
|
Guid? userId = null, |
||||
|
string userName = null, |
||||
|
string clientId = null, |
||||
|
string correlationId = null) |
||||
|
{ |
||||
|
return DbSet.AsNoTracking() |
||||
|
.WhereIf(startTime.HasValue, securityLog => securityLog.CreationTime >= startTime) |
||||
|
.WhereIf(endTime.HasValue, securityLog => securityLog.CreationTime >= endTime) |
||||
|
.WhereIf(!applicationName.IsNullOrWhiteSpace(), securityLog => securityLog.ApplicationName == applicationName) |
||||
|
.WhereIf(!identity.IsNullOrWhiteSpace(), securityLog => securityLog.Identity == identity) |
||||
|
.WhereIf(!action.IsNullOrWhiteSpace(), securityLog => securityLog.Action == action) |
||||
|
.WhereIf(userId.HasValue, securityLog => securityLog.UserId == userId) |
||||
|
.WhereIf(!userName.IsNullOrWhiteSpace(), securityLog => securityLog.UserName == userName) |
||||
|
.WhereIf(!clientId.IsNullOrWhiteSpace(), securityLog => securityLog.ClientId == clientId) |
||||
|
.WhereIf(!correlationId.IsNullOrWhiteSpace(), securityLog => securityLog.CorrelationId == correlationId); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,117 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Linq.Dynamic.Core; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using MongoDB.Driver; |
||||
|
using MongoDB.Driver.Linq; |
||||
|
using Volo.Abp.Domain.Repositories.MongoDB; |
||||
|
using Volo.Abp.MongoDB; |
||||
|
|
||||
|
namespace Volo.Abp.Identity.MongoDB |
||||
|
{ |
||||
|
public class MongoIdentitySecurityLogRepository : |
||||
|
MongoDbRepository<IAbpIdentityMongoDbContext, IdentitySecurityLog, Guid>, IIdentitySecurityLogRepository |
||||
|
{ |
||||
|
public MongoIdentitySecurityLogRepository(IMongoDbContextProvider<IAbpIdentityMongoDbContext> dbContextProvider) |
||||
|
: base(dbContextProvider) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public async Task<List<IdentitySecurityLog>> GetListAsync( |
||||
|
string sorting = null, |
||||
|
int maxResultCount = 50, |
||||
|
int skipCount = 0, |
||||
|
DateTime? startTime = null, |
||||
|
DateTime? endTime = null, |
||||
|
string applicationName = null, |
||||
|
string identity = null, |
||||
|
string action = null, |
||||
|
Guid? userId = null, |
||||
|
string userName = null, |
||||
|
string clientId = null, |
||||
|
string correlationId = null, |
||||
|
bool includeDetails = false, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var query = GetListQuery( |
||||
|
startTime, |
||||
|
endTime, |
||||
|
applicationName, |
||||
|
identity, |
||||
|
action, |
||||
|
userId, |
||||
|
userName, |
||||
|
clientId, |
||||
|
correlationId |
||||
|
); |
||||
|
|
||||
|
return await query.OrderBy(sorting ?? nameof(IdentitySecurityLog.CreationTime) + " desc") |
||||
|
.As<IMongoQueryable<IdentitySecurityLog>>() |
||||
|
.PageBy<IdentitySecurityLog, IMongoQueryable<IdentitySecurityLog>>(skipCount, maxResultCount) |
||||
|
.ToListAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
public async Task<long> GetCountAsync( |
||||
|
DateTime? startTime = null, |
||||
|
DateTime? endTime = null, |
||||
|
string applicationName = null, |
||||
|
string identity = null, |
||||
|
string action = null, |
||||
|
Guid? userId = null, |
||||
|
string userName = null, |
||||
|
string clientId = null, |
||||
|
string correlationId = null, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var query = GetListQuery( |
||||
|
startTime, |
||||
|
endTime, |
||||
|
applicationName, |
||||
|
identity, |
||||
|
action, |
||||
|
userId, |
||||
|
userName, |
||||
|
clientId, |
||||
|
correlationId |
||||
|
); |
||||
|
|
||||
|
return await query.As<IMongoQueryable<IdentitySecurityLog>>() |
||||
|
.LongCountAsync(GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public async Task<IdentitySecurityLog> GetByUserIdAsync(Guid id, Guid userId, bool includeDetails = false, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return await GetMongoQueryable().FirstOrDefaultAsync(x => x.Id == id && x.UserId == userId, |
||||
|
GetCancellationToken(cancellationToken)); |
||||
|
} |
||||
|
|
||||
|
protected virtual IQueryable<IdentitySecurityLog> GetListQuery( |
||||
|
DateTime? startTime = null, |
||||
|
DateTime? endTime = null, |
||||
|
string applicationName = null, |
||||
|
string identity = null, |
||||
|
string action = null, |
||||
|
Guid? userId = null, |
||||
|
string userName = null, |
||||
|
string clientId = null, |
||||
|
string correlationId = null) |
||||
|
{ |
||||
|
return GetMongoQueryable() |
||||
|
.WhereIf(startTime.HasValue, securityLog => securityLog.CreationTime >= startTime) |
||||
|
.WhereIf(endTime.HasValue, securityLog => securityLog.CreationTime >= endTime) |
||||
|
.WhereIf(!applicationName.IsNullOrWhiteSpace(), |
||||
|
securityLog => securityLog.ApplicationName == applicationName) |
||||
|
.WhereIf(!identity.IsNullOrWhiteSpace(), securityLog => securityLog.Identity == identity) |
||||
|
.WhereIf(!action.IsNullOrWhiteSpace(), securityLog => securityLog.Action == action) |
||||
|
.WhereIf(userId.HasValue, securityLog => securityLog.UserId == userId) |
||||
|
.WhereIf(!userName.IsNullOrWhiteSpace(), securityLog => securityLog.UserName == userName) |
||||
|
.WhereIf(!clientId.IsNullOrWhiteSpace(), securityLog => securityLog.ClientId == clientId) |
||||
|
.WhereIf(!correlationId.IsNullOrWhiteSpace(), |
||||
|
securityLog => securityLog.CorrelationId == correlationId); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp.Identity.EntityFrameworkCore |
||||
|
{ |
||||
|
public class IdentitySecurityLogRepository_Tests : IdentitySecurityLogRepository_Tests<AbpIdentityEntityFrameworkCoreTestModule> |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Identity.MongoDB |
||||
|
{ |
||||
|
[Collection(MongoTestCollection.Name)] |
||||
|
public class IdentitySecurityLogRepository_Tests : IdentitySecurityLogRepository_Tests<AbpIdentityMongoDbTestModule> |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Shouldly; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Identity |
||||
|
{ |
||||
|
public abstract class IdentitySecurityLogRepository_Tests<TStartupModule> : AbpIdentityTestBase<TStartupModule> |
||||
|
where TStartupModule : IAbpModule |
||||
|
{ |
||||
|
protected IIdentitySecurityLogRepository RoleRepository { get; } |
||||
|
protected IdentityTestData TestData { get; } |
||||
|
|
||||
|
protected IdentitySecurityLogRepository_Tests() |
||||
|
{ |
||||
|
RoleRepository = GetRequiredService<IIdentitySecurityLogRepository>(); |
||||
|
TestData = GetRequiredService<IdentityTestData>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task GetListAsync() |
||||
|
{ |
||||
|
var logs = await RoleRepository.GetListAsync(); |
||||
|
logs.ShouldNotBeEmpty(); |
||||
|
logs.ShouldContain(x => x.ApplicationName == "Test-ApplicationName" && x.UserId == TestData.UserJohnId); |
||||
|
logs.ShouldContain(x => x.ApplicationName == "Test-ApplicationName" && x.UserId == TestData.UserDavidId); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task GetCountAsync() |
||||
|
{ |
||||
|
var count = await RoleRepository.GetCountAsync(); |
||||
|
count.ShouldBe(2); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue