Constraint: 在线热修前必须保存当前迭代分支全部有效代码进展 Confidence: medium Scope-risk: broad Directive: 后续修改需保持审计事件与业务事务边界一致 Tested: git diff --cached --check Not-tested: 未运行全量测试,提交用于切换分支前保存既有工作
244 lines
7.3 KiB
Go
244 lines
7.3 KiB
Go
// Package accessaudit 定义账号权限与组织简单写用例的统一审计接缝。
|
|
package accessaudit
|
|
|
|
import (
|
|
"context"
|
|
stderrors "errors"
|
|
"strconv"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/break/junhong_cmp_fiber/internal/model"
|
|
"github.com/break/junhong_cmp_fiber/pkg/auditcontext"
|
|
"github.com/break/junhong_cmp_fiber/pkg/auditfailure"
|
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
|
apperrors "github.com/break/junhong_cmp_fiber/pkg/errors"
|
|
)
|
|
|
|
// ChangeAudit 是账号权限与组织变更交给统一审计 Port 的事实。
|
|
type ChangeAudit struct {
|
|
ActionCode string
|
|
Summary string
|
|
Result string
|
|
ErrorCode string
|
|
ErrorSummary string
|
|
OperatorID uint
|
|
ActorKind string
|
|
ActorName string
|
|
Source string
|
|
ScopeType string
|
|
Account *model.Account
|
|
Accounts []AccountChange
|
|
Shop *model.Shop
|
|
ParentShop *model.Shop
|
|
Enterprise *model.Enterprise
|
|
Cards []IotCardChange
|
|
CardAuthorizations []EnterpriseCardAuthorizationChange
|
|
Devices []DeviceChange
|
|
DeviceBindings []DeviceSimBindingChange
|
|
DeviceAuthorizations []EnterpriseDeviceAuthorizationChange
|
|
PersonalCustomer *model.PersonalCustomer
|
|
PersonalPhones []PersonalCustomerPhoneChange
|
|
PersonalOpenIDs []PersonalCustomerOpenIDChange
|
|
PersonalDevices []PersonalCustomerDeviceChange
|
|
PersonalICCIDs []PersonalCustomerICCIDChange
|
|
Role *model.Role
|
|
Roles []RoleChange
|
|
Permissions []PermissionChange
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
SubjectVisibility string
|
|
SubjectSummary string
|
|
SubjectData map[string]any
|
|
}
|
|
|
|
// PersonalCustomerPhoneChange 保存个人客户手机号资源变化。
|
|
type PersonalCustomerPhoneChange struct {
|
|
Phone *model.PersonalCustomerPhone
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
}
|
|
|
|
// PersonalCustomerOpenIDChange 保存个人客户微信主体资源变化。
|
|
type PersonalCustomerOpenIDChange struct {
|
|
OpenID *model.PersonalCustomerOpenID
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
}
|
|
|
|
// PersonalCustomerDeviceChange 保存个人客户设备号绑定资源变化。
|
|
type PersonalCustomerDeviceChange struct {
|
|
Binding *model.PersonalCustomerDevice
|
|
Relation string
|
|
Role string
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
}
|
|
|
|
// PersonalCustomerICCIDChange 保存个人客户 ICCID 绑定资源变化。
|
|
type PersonalCustomerICCIDChange struct {
|
|
Binding *model.PersonalCustomerICCID
|
|
Relation string
|
|
Role string
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
}
|
|
|
|
// DeviceChange 保存组织操作关联设备的资源变化与主体安全投影。
|
|
type DeviceChange struct {
|
|
Device *model.Device
|
|
Relation string
|
|
Role string
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
SubjectVisibility string
|
|
SubjectSummary string
|
|
SubjectData map[string]any
|
|
}
|
|
|
|
// DeviceSimBindingChange 保存企业设备授权涉及的卡槽绑定快照。
|
|
type DeviceSimBindingChange struct {
|
|
Binding *model.DeviceSimBinding
|
|
Relation string
|
|
Role string
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
}
|
|
|
|
// EnterpriseDeviceAuthorizationChange 保存企业设备授权记录的直接变化。
|
|
type EnterpriseDeviceAuthorizationChange struct {
|
|
Authorization *model.EnterpriseDeviceAuthorization
|
|
Relation string
|
|
Role string
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
}
|
|
|
|
// IotCardChange 保存组织操作关联卡的资源变化与主体安全投影。
|
|
type IotCardChange struct {
|
|
Card *model.IotCard
|
|
Relation string
|
|
Role string
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
SubjectVisibility string
|
|
SubjectSummary string
|
|
SubjectData map[string]any
|
|
}
|
|
|
|
// EnterpriseCardAuthorizationChange 保存企业卡授权记录的直接变化。
|
|
type EnterpriseCardAuthorizationChange struct {
|
|
Authorization *model.EnterpriseCardAuthorization
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
}
|
|
|
|
// AccountChange 保存店铺操作关联账号的资源角色与直接变化。
|
|
type AccountChange struct {
|
|
Account *model.Account
|
|
Relation string
|
|
Role string
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
}
|
|
|
|
// RoleChange 保存主体授权中单个角色资源的前后变化。
|
|
type RoleChange struct {
|
|
Role *model.Role
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
}
|
|
|
|
// PermissionChange 保存单个权限资源的前后变化。
|
|
type PermissionChange struct {
|
|
Permission *model.Permission
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
}
|
|
|
|
// Writer 接收账号权限与组织事务内审计事实。
|
|
type Writer interface {
|
|
WriteAccessChange(context.Context, *gorm.DB, ChangeAudit) error
|
|
}
|
|
|
|
// RecordFailure 在业务回滚后使用独立短事务记录失败或拒绝事实。
|
|
func RecordFailure(ctx context.Context, db *gorm.DB, writer Writer, change ChangeAudit, originalErr error) {
|
|
fillFailure(changeError(originalErr), &change)
|
|
if db == nil || writer == nil {
|
|
recordSecondaryFailure(ctx, change, apperrors.New(apperrors.CodeInvalidStatus, "统一组织审计接缝未配置"))
|
|
return
|
|
}
|
|
if err := db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
return writer.WriteAccessChange(ctx, tx, change)
|
|
}); err != nil {
|
|
recordSecondaryFailure(ctx, change, err)
|
|
}
|
|
}
|
|
|
|
func changeError(err error) *apperrors.AppError {
|
|
var appErr *apperrors.AppError
|
|
if stderrors.As(err, &appErr) {
|
|
return appErr
|
|
}
|
|
return apperrors.New(apperrors.CodeInternalError, "账号权限或组织操作失败")
|
|
}
|
|
|
|
func fillFailure(appErr *apperrors.AppError, change *ChangeAudit) {
|
|
if change.Result == "" {
|
|
change.Result = constants.AuditResultFailed
|
|
}
|
|
if change.ErrorCode == "" {
|
|
change.ErrorCode = strconv.Itoa(appErr.Code)
|
|
}
|
|
if change.ErrorSummary == "" {
|
|
change.ErrorSummary = appErr.Message
|
|
}
|
|
}
|
|
|
|
func recordSecondaryFailure(ctx context.Context, change ChangeAudit, err error) {
|
|
value := auditcontext.From(ctx)
|
|
auditfailure.RecordSecondaryWriteFailure(
|
|
change.ActionCode, resourceKey(change), value.RequestID, value.CorrelationID, change.ErrorCode, err,
|
|
)
|
|
}
|
|
|
|
func resourceKey(change ChangeAudit) string {
|
|
if change.Account != nil {
|
|
if change.Account.ID != 0 {
|
|
return strconv.FormatUint(uint64(change.Account.ID), 10)
|
|
}
|
|
return change.Account.Username
|
|
}
|
|
if change.Enterprise != nil {
|
|
if change.Enterprise.ID != 0 {
|
|
return strconv.FormatUint(uint64(change.Enterprise.ID), 10)
|
|
}
|
|
return change.Enterprise.EnterpriseCode
|
|
}
|
|
if change.PersonalCustomer != nil {
|
|
return strconv.FormatUint(uint64(change.PersonalCustomer.ID), 10)
|
|
}
|
|
if change.Shop != nil {
|
|
if change.Shop.ID != 0 {
|
|
return strconv.FormatUint(uint64(change.Shop.ID), 10)
|
|
}
|
|
return change.Shop.ShopCode
|
|
}
|
|
if change.Role != nil {
|
|
if change.Role.ID != 0 {
|
|
return strconv.FormatUint(uint64(change.Role.ID), 10)
|
|
}
|
|
return change.Role.RoleName
|
|
}
|
|
for _, permission := range change.Permissions {
|
|
if permission.Permission == nil {
|
|
continue
|
|
}
|
|
if permission.Permission.ID != 0 {
|
|
return strconv.FormatUint(uint64(permission.Permission.ID), 10)
|
|
}
|
|
return permission.Permission.PermCode
|
|
}
|
|
return "unknown"
|
|
}
|