diff --git a/Volo.Abp.sln b/Volo.Abp.sln
index cf2f59c46e..82d3796336 100644
--- a/Volo.Abp.sln
+++ b/Volo.Abp.sln
@@ -308,6 +308,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Users", "src\Volo.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Auditing", "src\Volo.Abp.Auditing\Volo.Abp.Auditing.csproj", "{03F51721-DA51-4BAE-9909-3FC88FAB7774}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Auditing.Tests", "test\Volo.Abp.Auditing.Tests\Volo.Abp.Auditing.Tests.csproj", "{D5733D90-8C3D-4026-85E2-41DED26C4938}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -814,6 +816,10 @@ Global
{03F51721-DA51-4BAE-9909-3FC88FAB7774}.Debug|Any CPU.Build.0 = Debug|Any CPU
{03F51721-DA51-4BAE-9909-3FC88FAB7774}.Release|Any CPU.ActiveCfg = Release|Any CPU
{03F51721-DA51-4BAE-9909-3FC88FAB7774}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D5733D90-8C3D-4026-85E2-41DED26C4938}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D5733D90-8C3D-4026-85E2-41DED26C4938}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D5733D90-8C3D-4026-85E2-41DED26C4938}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D5733D90-8C3D-4026-85E2-41DED26C4938}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -964,6 +970,7 @@ Global
{F33F751F-1260-40C0-8F76-E3D680605662} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{DAE9088E-6E30-4D5D-9DF8-2188E8D386FE} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8}
{03F51721-DA51-4BAE-9909-3FC88FAB7774} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8}
+ {D5733D90-8C3D-4026-85E2-41DED26C4938} = {37087D1B-3693-4E96-983D-A69F210BDE53}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}
diff --git a/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj b/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj
index 450efb7de6..04549d5441 100644
--- a/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj
+++ b/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj
@@ -14,8 +14,10 @@
-
+
+
+
diff --git a/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj.DotSettings b/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj.DotSettings
new file mode 100644
index 0000000000..58ad6c8854
--- /dev/null
+++ b/src/Volo.Abp.Auditing/Volo.Abp.Auditing.csproj.DotSettings
@@ -0,0 +1,2 @@
+
+ CSharp71
\ No newline at end of file
diff --git a/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs b/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
index 42532bfb3a..13202af3fe 100644
--- a/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
+++ b/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingModule.cs
@@ -1,10 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.Modularity;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Timing;
+using Volo.Abp.Users;
namespace Volo.Abp.Auditing
{
- [DependsOn(typeof(AbpDataModule))] //TODO: Can we remove data module dependency since it only contains ISoftDelete related to auditing module!
+ //TODO: Can we remove AbpDataModule dependency since it only contains ISoftDelete related to auditing module!
+ [DependsOn(
+ typeof(AbpDataModule),
+ typeof(AbpTimingModule),
+ typeof(AbpUsersModule),
+ typeof(AbpMultiTenancyAbstractionsModule)
+ )]
public class AbpAuditingModule : AbpModule
{
public override void ConfigureServices(IServiceCollection services)
diff --git a/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs b/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs
new file mode 100644
index 0000000000..715fcbc058
--- /dev/null
+++ b/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditPropertySetter.cs
@@ -0,0 +1,111 @@
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Timing;
+using Volo.Abp.Users;
+
+namespace Volo.Abp.Auditing
+{
+ public class AuditPropertySetter : IAuditPropertySetter, ITransientDependency
+ {
+ protected ICurrentUser CurrentUser { get; }
+
+ protected ICurrentTenant CurrentTenant { get; }
+
+ protected IClock Clock { get; }
+
+ public AuditPropertySetter(
+ ICurrentUser currentUser,
+ ICurrentTenant currentTenant,
+ IClock clock)
+ {
+ CurrentUser = currentUser;
+ CurrentTenant = currentTenant;
+ Clock = clock;
+ }
+
+ public void SetCreationAuditProperties(object targetObject)
+ {
+ if (!(targetObject is IHasCreationTime objectWithCreationTime))
+ {
+ return;
+ }
+
+ if (objectWithCreationTime.CreationTime == default)
+ {
+ objectWithCreationTime.CreationTime = Clock.Now;
+ }
+
+ if (!(targetObject is ICreationAudited creationAuditedObject))
+ {
+ return;
+ }
+
+ if (creationAuditedObject.CreatorId != null)
+ {
+ return;
+ }
+
+ if (!CurrentUser.Id.HasValue)
+ {
+ return;
+ }
+
+ if (targetObject is IMultiTenant multiTenantEntity)
+ {
+ if (multiTenantEntity.TenantId != CurrentUser.TenantId)
+ {
+ return;
+ }
+ }
+
+ /* TODO: The code below is from old ABP, not implemented yet
+ if (tenantId.HasValue && MultiTenancyHelper.IsHostEntity(entity))
+ {
+ //Tenant user created a host entity
+ return;
+ }
+ */
+
+ creationAuditedObject.CreatorId = CurrentUser.Id;
+ }
+
+ public void SetModificationAuditProperties(object auditedObject)
+ {
+ if (auditedObject is IHasModificationTime objectWithModificationTime)
+ {
+ objectWithModificationTime.LastModificationTime = Clock.Now;
+ }
+
+ if (!(auditedObject is IModificationAudited modificationAuditedObject))
+ {
+ return;
+ }
+
+ if (!CurrentUser.Id.HasValue)
+ {
+ modificationAuditedObject.LastModifierId = null;
+ return;
+ }
+
+ if (modificationAuditedObject is IMultiTenant multiTenantEntity)
+ {
+ if (multiTenantEntity.TenantId != CurrentUser.TenantId)
+ {
+ modificationAuditedObject.LastModifierId = null;
+ return;
+ }
+ }
+
+ /* TODO: The code below is from old ABP, not implemented yet
+ if (tenantId.HasValue && MultiTenancyHelper.IsHostEntity(entity))
+ {
+ //Tenant user modified a host entity
+ modificationAuditedObject.LastModifierId = null;
+ return;
+ }
+ */
+
+ modificationAuditedObject.LastModifierId = CurrentUser.Id;
+ }
+ }
+}
diff --git a/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditPropertySetter.cs b/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditPropertySetter.cs
new file mode 100644
index 0000000000..f8934fd895
--- /dev/null
+++ b/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditPropertySetter.cs
@@ -0,0 +1,9 @@
+namespace Volo.Abp.Auditing
+{
+ public interface IAuditPropertySetter
+ {
+ void SetCreationAuditProperties(object targetObject);
+
+ void SetModificationAuditProperties(object auditedObject);
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.Users/Volo/Abp/Users/CurrentUser.cs b/src/Volo.Abp.Users/Volo/Abp/Users/CurrentUser.cs
index abcde115ab..884c9078f1 100644
--- a/src/Volo.Abp.Users/Volo/Abp/Users/CurrentUser.cs
+++ b/src/Volo.Abp.Users/Volo/Abp/Users/CurrentUser.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Security.Claims;
+using System.Security.Principal;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Claims;
@@ -12,19 +13,7 @@ namespace Volo.Abp.Users
public virtual bool IsAuthenticated => Id.HasValue;
- public virtual Guid? Id
- {
- get
- {
- var value = this.FindClaimValue(AbpClaimTypes.UserId);
- if (value == null)
- {
- return null;
- }
-
- return Guid.Parse(value);
- }
- }
+ public virtual Guid? Id => _principalAccessor.Principal?.FindUserId();
public virtual string UserName => this.FindClaimValue(AbpClaimTypes.UserName);
@@ -36,6 +25,8 @@ namespace Volo.Abp.Users
public virtual bool EmailVerified => string.Equals(this.FindClaimValue(AbpClaimTypes.EmailVerified), "true", StringComparison.InvariantCultureIgnoreCase);
+ public virtual Guid? TenantId => _principalAccessor.Principal?.FindTenantId();
+
public virtual string[] Roles => FindClaims(AbpClaimTypes.Role).Select(c => c.Value).ToArray();
private readonly ICurrentPrincipalAccessor _principalAccessor;
diff --git a/src/Volo.Abp.Users/Volo/Abp/Users/ICurrentUser.cs b/src/Volo.Abp.Users/Volo/Abp/Users/ICurrentUser.cs
index 46bcb7d308..d2aebf8e93 100644
--- a/src/Volo.Abp.Users/Volo/Abp/Users/ICurrentUser.cs
+++ b/src/Volo.Abp.Users/Volo/Abp/Users/ICurrentUser.cs
@@ -16,7 +16,7 @@ namespace Volo.Abp.Users
[CanBeNull]
string PhoneNumber { get; }
-
+
bool PhoneNumberVerified { get; }
[CanBeNull]
@@ -24,6 +24,8 @@ namespace Volo.Abp.Users
bool EmailVerified { get; }
+ Guid? TenantId { get; }
+
[NotNull]
string[] Roles { get; }
diff --git a/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj b/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj
new file mode 100644
index 0000000000..ac881ee3af
--- /dev/null
+++ b/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj
@@ -0,0 +1,24 @@
+
+
+
+ netcoreapp2.0
+ latest
+ Volo.Abp.Auditing.Tests
+ Volo.Abp.Auditing.Tests
+ true
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj.DotSettings b/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj.DotSettings
new file mode 100644
index 0000000000..58ad6c8854
--- /dev/null
+++ b/test/Volo.Abp.Auditing.Tests/Volo.Abp.Auditing.Tests.csproj.DotSettings
@@ -0,0 +1,2 @@
+
+ CSharp71
\ No newline at end of file
diff --git a/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetterTestBase.cs b/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetterTestBase.cs
new file mode 100644
index 0000000000..4bdad632be
--- /dev/null
+++ b/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetterTestBase.cs
@@ -0,0 +1,57 @@
+using System;
+using NSubstitute;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Timing;
+using Volo.Abp.Users;
+
+namespace Volo.Abp.Auditing
+{
+ public class AuditPropertySetterTestBase
+ {
+ protected Guid? CurrentUserId = null;
+ protected Guid? CurrentUserTenantId = null;
+ protected Guid? CurrentTenantId = null;
+
+ protected DateTime Now = DateTime.Now;
+
+ protected MyAuditedObject TargetObject;
+
+ protected readonly AuditPropertySetter AuditPropertySetter;
+
+ public AuditPropertySetterTestBase()
+ {
+ AuditPropertySetter = CreateAuditPropertySetter();
+ TargetObject = new MyAuditedObject();
+ }
+
+ private AuditPropertySetter CreateAuditPropertySetter()
+ {
+ var currentUser = Substitute.For();
+ currentUser.Id.Returns(ci => CurrentUserId);
+ currentUser.TenantId.Returns(ci => CurrentUserTenantId);
+
+ var currentTenant = Substitute.For();
+ currentTenant.Id.Returns(ci => CurrentTenantId);
+
+ var clock = Substitute.For();
+ clock.Now.Returns(Now);
+
+ return new AuditPropertySetter(
+ currentUser,
+ currentTenant,
+ clock
+ );
+ }
+
+ public class MyAuditedObject : IFullAudited
+ {
+ public DateTime CreationTime { get; set; }
+ public Guid? CreatorId { get; set; }
+ public DateTime? LastModificationTime { get; set; }
+ public Guid? LastModifierId { get; set; }
+ public bool IsDeleted { get; set; }
+ public DateTime? DeletionTime { get; set; }
+ public Guid? DeleterId { get; set; }
+ }
+ }
+}
diff --git a/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetter_CreationAudit_Tests.cs b/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetter_CreationAudit_Tests.cs
new file mode 100644
index 0000000000..5ea1b120e5
--- /dev/null
+++ b/test/Volo.Abp.Auditing.Tests/Volo/Abp/Auditing/AuditPropertySetter_CreationAudit_Tests.cs
@@ -0,0 +1,28 @@
+using System;
+using Shouldly;
+using Xunit;
+
+namespace Volo.Abp.Auditing
+{
+ public class AuditPropertySetter_CreationAudit_Tests : AuditPropertySetterTestBase
+ {
+ [Fact]
+ public void Should_Set_CreationTime()
+ {
+ AuditPropertySetter.SetCreationAuditProperties(TargetObject);
+
+ TargetObject.CreationTime.ShouldNotBe(default);
+ }
+
+ [Fact]
+ public void Should_Set_CreatorId()
+ {
+ CurrentUserId = Guid.NewGuid();
+
+ AuditPropertySetter.SetCreationAuditProperties(TargetObject);
+
+ TargetObject.CreationTime.ShouldNotBe(default);
+ TargetObject.CreatorId.ShouldBe(CurrentUserId);
+ }
+ }
+}
\ No newline at end of file