Browse Source

feat: Optimize audit logs

pull/1527/head
colin 3 months ago
parent
commit
b463e102a7
  1. 34
      apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogDrawer.vue
  2. 2
      apps/vben5/packages/@abp/auditing/src/types/audit-logs.ts
  3. 15
      apps/vben5/packages/@abp/identity/src/components/sessions/SessionTable.vue
  4. 91
      apps/vben5/packages/@abp/identity/src/components/users/UserTable.vue
  5. 7
      apps/vben5/packages/@abp/identity/src/types/sessions.ts
  6. 1
      aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.Elasticsearch/LINGYUN.Abp.AuditLogging.Elasticsearch.csproj
  7. 4
      aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging/LINGYUN/Abp/AuditLogging/AuditLogQueue.cs
  8. 4
      aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Application.Contracts/LINGYUN/Abp/Auditing/AuditLogs/AuditLogDto.cs
  9. 1
      aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Application.Contracts/LINGYUN/Abp/Auditing/Features/AuditingFeatureDefinitionProvider.cs

34
apps/vben5/packages/@abp/auditing/src/components/audit-logs/AuditLogDrawer.vue

@ -148,17 +148,43 @@ async function onGet(id: string) {
<DescriptionsItem :label="$t('AbpAuditLogging.UserName')"> <DescriptionsItem :label="$t('AbpAuditLogging.UserName')">
{{ auditLogModel.userName }} {{ auditLogModel.userName }}
</DescriptionsItem> </DescriptionsItem>
<DescriptionsItem :label="$t('AbpAuditLogging.HttpMethod')"> <DescriptionsItem :label="$t('AbpAuditLogging.TenantName')">
<Tag :color="getHttpMethodColor(auditLogModel.httpMethod)"> <span v-if="auditLogModel.tenantId">
{{ auditLogModel.httpMethod }} {{ auditLogModel.tenantId }}/{{ auditLogModel.tenantName }}
</Tag> </span>
</DescriptionsItem> </DescriptionsItem>
<template v-if="auditLogModel.impersonatorUserName">
<DescriptionsItem
:label="$t('AbpAuditLogging.ImpersonatorTenantId')"
>
<span v-if="auditLogModel.impersonatorTenantId">
{{ auditLogModel.impersonatorTenantId }}/{{
auditLogModel.impersonatorTenantName
}}
</span>
</DescriptionsItem>
<DescriptionsItem
:label="$t('AbpAuditLogging.ImpersonatorUserId')"
>
{{ auditLogModel.impersonatorUserId }}/{{
auditLogModel.impersonatorUserName
}}
</DescriptionsItem>
</template>
<DescriptionsItem <DescriptionsItem
:label="$t('AbpAuditLogging.RequestUrl')" :label="$t('AbpAuditLogging.RequestUrl')"
:span="2" :span="2"
> >
{{ auditLogModel.url }} {{ auditLogModel.url }}
</DescriptionsItem> </DescriptionsItem>
<DescriptionsItem
:label="$t('AbpAuditLogging.HttpMethod')"
:span="2"
>
<Tag :color="getHttpMethodColor(auditLogModel.httpMethod)">
{{ auditLogModel.httpMethod }}
</Tag>
</DescriptionsItem>
<DescriptionsItem :label="$t('AbpAuditLogging.HttpStatusCode')"> <DescriptionsItem :label="$t('AbpAuditLogging.HttpStatusCode')">
<Tag <Tag
:color="getHttpStatusCodeColor(auditLogModel.httpStatusCode)" :color="getHttpStatusCodeColor(auditLogModel.httpStatusCode)"

2
apps/vben5/packages/@abp/auditing/src/types/audit-logs.ts

@ -39,7 +39,9 @@ interface AuditLogDto {
httpStatusCode?: number; httpStatusCode?: number;
id: string; id: string;
impersonatorTenantId?: string; impersonatorTenantId?: string;
impersonatorTenantName?: string;
impersonatorUserId?: string; impersonatorUserId?: string;
impersonatorUserName?: string;
tenantId?: string; tenantId?: string;
tenantName?: string; tenantName?: string;
url?: string; url?: string;

15
apps/vben5/packages/@abp/identity/src/components/sessions/SessionTable.vue

@ -85,6 +85,19 @@ const gridOptions: VxeGridProps<IdentitySessionDto> = {
sortable: true, sortable: true,
title: $t('AbpIdentity.DisplayName:SessionId'), title: $t('AbpIdentity.DisplayName:SessionId'),
}, },
{
align: 'left',
field: 'userId',
formatter: ({ row }) => {
if (row.extraProperties?.UserName) {
return `${row.extraProperties.UserName} (${row.userId})`;
}
return row.userId;
},
minWidth: 150,
sortable: true,
title: $t('AbpIdentity.DisplayName:UserId'),
},
{ {
align: 'left', align: 'left',
field: 'device', field: 'device',
@ -98,7 +111,7 @@ const gridOptions: VxeGridProps<IdentitySessionDto> = {
field: 'deviceInfo', field: 'deviceInfo',
sortable: true, sortable: true,
title: $t('AbpIdentity.DisplayName:DeviceInfo'), title: $t('AbpIdentity.DisplayName:DeviceInfo'),
width: 'auto', minWidth: 200,
}, },
{ {
align: 'left', align: 'left',

91
apps/vben5/packages/@abp/identity/src/components/users/UserTable.vue

@ -61,20 +61,68 @@ const PermissionsOutlined = createIconifyIcon('icon-park-outline:permissions');
const SessionIcon = createIconifyIcon('carbon:prompt-session'); const SessionIcon = createIconifyIcon('carbon:prompt-session');
const AuditLogIcon = createIconifyIcon('fluent-mdl2:compliance-audit'); const AuditLogIcon = createIconifyIcon('fluent-mdl2:compliance-audit');
const getLockEnd = computed(() => { const isCurrentUser = (user: IdentityUserDto) => {
return (row: IdentityUserDto) => { return abpStore.application?.currentUser.id === user.id;
if (row.lockoutEnd) { };
const lockTime = new Date(row.lockoutEnd);
if (lockTime) { const getLockEnd = (row: IdentityUserDto) => {
// 锁定时间高于当前时间不显示 if (row.lockoutEnd) {
const nowTime = new Date(); const lockTime = new Date(row.lockoutEnd);
return lockTime < nowTime; if (lockTime) {
} // 锁定时间高于当前时间不显示
const nowTime = new Date();
return lockTime < nowTime;
} }
return true; }
return true;
};
const isDeleteEnabled = computed(() => {
return (user: IdentityUserDto) => {
return (
!isCurrentUser(user) && hasAccessByCodes([IdentityUserPermissions.Delete])
);
};
});
const isLockEnabled = computed(() => {
return (user: IdentityUserDto) => {
return (
!isCurrentUser(user) &&
user.isActive &&
getLockEnd(user) &&
hasAccessByCodes([IdentityUserPermissions.Update])
);
};
});
const isUnLockEnabled = computed(() => {
return (user: IdentityUserDto) => {
return (
!isCurrentUser(user) &&
user.isActive &&
!getLockEnd(user) &&
hasAccessByCodes([IdentityUserPermissions.Update])
);
};
});
const isImpersonationEnabled = computed(() => {
return (user: IdentityUserDto) => {
return (
!isCurrentUser(user) &&
hasAccessByCodes([IdentityUserPermissions.Impersonation])
);
}; };
}); });
const isAuditLogEnabled = computed(() => {
return (
isEnabled('AbpAuditing.Logging.AuditLog') &&
hasAccessByCodes([AuditLogPermissions.Default])
);
});
const abpStore = useAbpStore(); const abpStore = useAbpStore();
const { isEnabled } = useFeatures(); const { isEnabled } = useFeatures();
const { hasAccessByCodes } = useAccess(); const { hasAccessByCodes } = useAccess();
@ -385,7 +433,7 @@ const handleMenuClick = async (row: IdentityUserDto, info: MenuInfo) => {
block block
danger danger
type="link" type="link"
v-access:code="[IdentityUserPermissions.Delete]" :disabled="!isDeleteEnabled(row)"
@click="handleDelete(row)" @click="handleDelete(row)"
> >
{{ $t('AbpUi.Delete') }} {{ $t('AbpUi.Delete') }}
@ -396,22 +444,14 @@ const handleMenuClick = async (row: IdentityUserDto, info: MenuInfo) => {
<template #overlay> <template #overlay>
<Menu @click="(info: MenuInfo) => handleMenuClick(row, info)"> <Menu @click="(info: MenuInfo) => handleMenuClick(row, info)">
<MenuItem <MenuItem
v-if=" v-if="isLockEnabled(row)"
hasAccessByCodes([IdentityUserPermissions.Update]) &&
row.isActive &&
getLockEnd(row)
"
key="lock" key="lock"
:icon="h(LockOutlined)" :icon="h(LockOutlined)"
> >
{{ $t('AbpIdentity.Lock') }} {{ $t('AbpIdentity.Lock') }}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
v-if=" v-if="isUnLockEnabled(row)"
hasAccessByCodes([IdentityUserPermissions.Update]) &&
row.isActive &&
!getLockEnd(row)
"
key="unlock" key="unlock"
:icon="h(UnlockOutlined)" :icon="h(UnlockOutlined)"
> >
@ -446,9 +486,7 @@ const handleMenuClick = async (row: IdentityUserDto, info: MenuInfo) => {
{{ $t('AbpIdentity.ManageClaim') }} {{ $t('AbpIdentity.ManageClaim') }}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
v-if=" v-if="isImpersonationEnabled(row)"
hasAccessByCodes([IdentityUserPermissions.Impersonation])
"
key="impersonation" key="impersonation"
:icon="h(LoginOutlined)" :icon="h(LoginOutlined)"
> >
@ -472,10 +510,7 @@ const handleMenuClick = async (row: IdentityUserDto, info: MenuInfo) => {
</MenuItem> </MenuItem>
<MenuDivider /> <MenuDivider />
<MenuItem <MenuItem
v-if=" v-if="isAuditLogEnabled"
isEnabled('AbpAuditing.Logging.AuditLog') &&
hasAccessByCodes([AuditLogPermissions.Default])
"
key="entity-changes" key="entity-changes"
:icon="h(AuditLogIcon)" :icon="h(AuditLogIcon)"
> >

7
apps/vben5/packages/@abp/identity/src/types/sessions.ts

@ -1,6 +1,9 @@
import type { EntityDto, PagedAndSortedResultRequestDto } from '@abp/core'; import type {
ExtensibleEntityDto,
PagedAndSortedResultRequestDto,
} from '@abp/core';
interface IdentitySessionDto extends EntityDto<string> { interface IdentitySessionDto extends ExtensibleEntityDto<string> {
clientId?: string; clientId?: string;
device: string; device: string;
deviceInfo: string; deviceInfo: string;

1
aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging.Elasticsearch/LINGYUN.Abp.AuditLogging.Elasticsearch.csproj

@ -15,7 +15,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Volo.Abp.Json" /> <PackageReference Include="Volo.Abp.Json" />
<PackageReference Include="System.Threading.Channels" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

4
aspnet-core/framework/auditing/LINGYUN.Abp.AuditLogging/LINGYUN/Abp/AuditLogging/AuditLogQueue.cs

@ -30,7 +30,7 @@ public class AuditLogQueue : AuditLoggingQueue<AuditLogInfo>, IAuditLogQueue, IS
protected async override Task BulkWriteAsync(IEnumerable<AuditLogInfo> auditLogInfos, CancellationToken cancellationToken = default) protected async override Task BulkWriteAsync(IEnumerable<AuditLogInfo> auditLogInfos, CancellationToken cancellationToken = default)
{ {
var tenantAuditlogGroup = auditLogInfos.GroupBy(x => x.ImpersonatorTenantId ?? x.TenantId); var tenantAuditlogGroup = auditLogInfos.GroupBy(x => x.TenantId);
foreach (var tenantAuditlogs in tenantAuditlogGroup) foreach (var tenantAuditlogs in tenantAuditlogGroup)
{ {
using (_currentTenant.Change(tenantAuditlogs.Key)) using (_currentTenant.Change(tenantAuditlogs.Key))
@ -42,7 +42,7 @@ public class AuditLogQueue : AuditLoggingQueue<AuditLogInfo>, IAuditLogQueue, IS
protected async override Task WriteAsync(AuditLogInfo auditLogInfo, CancellationToken cancellationToken = default) protected async override Task WriteAsync(AuditLogInfo auditLogInfo, CancellationToken cancellationToken = default)
{ {
using (_currentTenant.Change(auditLogInfo.ImpersonatorTenantId ?? auditLogInfo.TenantId)) using (_currentTenant.Change(auditLogInfo.TenantId))
{ {
await _auditLogWriter.WriteAsync(auditLogInfo, cancellationToken); await _auditLogWriter.WriteAsync(auditLogInfo, cancellationToken);
} }

4
aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Application.Contracts/LINGYUN/Abp/Auditing/AuditLogs/AuditLogDto.cs

@ -18,8 +18,12 @@ public class AuditLogDto : ExtensibleEntityDto<Guid>
public Guid? ImpersonatorUserId { get; set; } public Guid? ImpersonatorUserId { get; set; }
public string ImpersonatorUserName { get; set; }
public Guid? ImpersonatorTenantId { get; set; } public Guid? ImpersonatorTenantId { get; set; }
public string ImpersonatorTenantName { get; set; }
public DateTime ExecutionTime { get; set; } public DateTime ExecutionTime { get; set; }
public int ExecutionDuration { get; set; } public int ExecutionDuration { get; set; }

1
aspnet-core/modules/auditing/LINGYUN.Abp.Auditing.Application.Contracts/LINGYUN/Abp/Auditing/Features/AuditingFeatureDefinitionProvider.cs

@ -15,6 +15,7 @@ public class AuditingFeatureDefinitionProvider : FeatureDefinitionProvider
var loggingEnableFeature = auditingGroup.AddFeature( var loggingEnableFeature = auditingGroup.AddFeature(
name: AuditingFeatureNames.Logging.Enable, name: AuditingFeatureNames.Logging.Enable,
defaultValue: true.ToString(),
displayName: L("Features:Auditing"), displayName: L("Features:Auditing"),
description: L("Features:AuditingDesc") description: L("Features:AuditingDesc")
); );

Loading…
Cancel
Save