mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 07:20:24 +08:00
refactor: 授权凭证优化
This commit is contained in:
@@ -2,10 +2,13 @@ import EnumValue from './Enum';
|
||||
|
||||
// 标签关联的资源类型
|
||||
export const TagResourceTypeEnum = {
|
||||
AuthCert: EnumValue.of(-2, '公共凭证').setExtra({ icon: 'Ticket' }),
|
||||
Tag: EnumValue.of(-1, '标签').setExtra({ icon: 'CollectionTag' }),
|
||||
|
||||
Machine: EnumValue.of(1, '机器').setExtra({ icon: 'Monitor' }),
|
||||
Db: EnumValue.of(2, '数据库').setExtra({ icon: 'Coin' }),
|
||||
Redis: EnumValue.of(3, 'redis').setExtra({ icon: 'iconfont icon-redis' }),
|
||||
Mongo: EnumValue.of(4, 'mongo').setExtra({ icon: 'iconfont icon-mongo' }),
|
||||
|
||||
MachineAuthCert: EnumValue.of(11, '机器-授权凭证').setExtra({ icon: 'Ticket' }),
|
||||
};
|
||||
|
||||
@@ -2,26 +2,8 @@
|
||||
<div class="auth-cert-edit">
|
||||
<el-dialog title="凭证保存" v-model="dialogVisible" :show-close="false" width="500px" :destroy-on-close="true" :close-on-click-modal="false">
|
||||
<el-form ref="acForm" :model="state.form" label-width="auto" :rules="rules">
|
||||
<el-form-item prop="ciphertextType" label="密文类型" required>
|
||||
<el-select
|
||||
:disabled="form.id && props.resourceEdit"
|
||||
v-model="form.ciphertextType"
|
||||
placeholder="请选择密文类型"
|
||||
@change="changeCiphertextType"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in AuthCertCiphertextTypeEnum"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
v-show="!props.disableCiphertextType?.includes(item.value)"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="type" label="凭证类型" required>
|
||||
<el-select style="width: 100%" v-model="form.type" placeholder="请选择凭证类型">
|
||||
<el-select @change="changeType" v-model="form.type" placeholder="请选择凭证类型">
|
||||
<el-option
|
||||
v-for="item in AuthCertTypeEnum"
|
||||
:key="item.value"
|
||||
@@ -33,13 +15,44 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="ciphertextType" label="密文类型" required>
|
||||
<el-select v-model="form.ciphertextType" placeholder="请选择密文类型" @change="changeCiphertextType">
|
||||
<el-option
|
||||
v-for="item in AuthCertCiphertextTypeEnum"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
v-show="!props.disableCiphertextType?.includes(item.value)"
|
||||
:disabled="item.value == AuthCertCiphertextTypeEnum.Public.value && form.type == AuthCertTypeEnum.Public.value"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="showResourceEdit">
|
||||
<el-form-item prop="type" label="资源类型" required>
|
||||
<el-select :disabled="form.id" v-model="form.resourceType" placeholder="请选择资源类型">
|
||||
<el-option
|
||||
:key="TagResourceTypeEnum.Machine.value"
|
||||
:label="TagResourceTypeEnum.Machine.label"
|
||||
:value="TagResourceTypeEnum.Machine.value"
|
||||
/>
|
||||
|
||||
<el-option :key="TagResourceTypeEnum.Db.value" :label="TagResourceTypeEnum.Db.label" :value="TagResourceTypeEnum.Db.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="resourceCode" label="资源编号" required>
|
||||
<el-input :disabled="form.id" v-model="form.resourceCode" placeholder="请输入资源编号"></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item prop="name" label="名称" required>
|
||||
<el-input :disabled="form.id" v-model="form.name"></el-input>
|
||||
<el-input :disabled="form.id" v-model="form.name" placeholder="请输入凭证名 (全局唯一)"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="form.ciphertextType != AuthCertCiphertextTypeEnum.Public.value">
|
||||
<el-form-item prop="username" label="用户名">
|
||||
<el-input :disabled="form.id && props.resourceEdit" v-model="form.username"></el-input>
|
||||
<el-input v-model="form.username"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.ciphertextType == AuthCertCiphertextTypeEnum.Password.value" prop="ciphertext" label="密码">
|
||||
@@ -97,11 +110,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, toRefs, onMounted, watch } from 'vue';
|
||||
import { reactive, ref, toRefs, onMounted, watch, computed } from 'vue';
|
||||
import { AuthCertTypeEnum, AuthCertCiphertextTypeEnum } from '../tag/enums';
|
||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
|
||||
import { resourceAuthCertApi } from '../tag/api';
|
||||
import { ResourceCodePattern } from '@/common/pattern';
|
||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||
|
||||
const props = defineProps({
|
||||
authCert: {
|
||||
@@ -113,7 +127,7 @@ const props = defineProps({
|
||||
disableType: {
|
||||
type: Array,
|
||||
},
|
||||
// 是否为资源编辑该授权凭证
|
||||
// 是否为资源编辑该授权凭证,即机器编辑等页面等
|
||||
resourceEdit: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
@@ -126,6 +140,8 @@ const DefaultForm = {
|
||||
username: '',
|
||||
ciphertextType: AuthCertCiphertextTypeEnum.Password.value,
|
||||
type: AuthCertTypeEnum.Private.value,
|
||||
resourceType: TagResourceTypeEnum.AuthCert.value,
|
||||
resourceCode: '',
|
||||
ciphertext: '',
|
||||
extra: {} as any,
|
||||
remark: '',
|
||||
@@ -158,6 +174,10 @@ const state = reactive({
|
||||
publicAuthCerts: [] as any,
|
||||
});
|
||||
|
||||
const showResourceEdit = computed(() => {
|
||||
return state.form.type != AuthCertTypeEnum.Public.value && !props.resourceEdit;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
setForm(props.authCert);
|
||||
});
|
||||
@@ -170,6 +190,7 @@ watch(
|
||||
);
|
||||
|
||||
const setForm = (val: any) => {
|
||||
val = { ...val };
|
||||
if (!val.extra) {
|
||||
val.extra = {};
|
||||
}
|
||||
@@ -181,8 +202,16 @@ const setForm = (val: any) => {
|
||||
|
||||
const { form, btnLoading } = toRefs(state);
|
||||
|
||||
const changeType = (val: any) => {
|
||||
// 如果选择了公共凭证,则需要保证密文类型不能为公共凭证
|
||||
if (val == AuthCertTypeEnum.Public.value && state.form.ciphertextType == AuthCertCiphertextTypeEnum.Public.value) {
|
||||
state.form.ciphertextType = AuthCertCiphertextTypeEnum.Password.value;
|
||||
}
|
||||
};
|
||||
|
||||
const changeCiphertextType = (val: any) => {
|
||||
if (val == AuthCertCiphertextTypeEnum.Public.value) {
|
||||
state.form.type = AuthCertTypeEnum.Private.value;
|
||||
getPublicAuthCerts();
|
||||
}
|
||||
};
|
||||
@@ -211,6 +240,7 @@ const cancelEdit = () => {
|
||||
dialogVisible.value = false;
|
||||
setTimeout(() => {
|
||||
state.form = { ...DefaultForm };
|
||||
acForm.value?.resetFields();
|
||||
}, 300);
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<el-button v-auth="'authcert:save'" class="ml0" type="primary" circle size="small" icon="Plus" @click="edit(null)"> </el-button>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-button v-auth="'authcert:save'" @click="edit({ ...scope.row }, scope.$index)" type="primary" icon="edit" link></el-button>
|
||||
<el-button v-auth="'authcert:save'" @click="edit(scope.row, scope.$index)" type="primary" icon="edit" link></el-button>
|
||||
<el-button class="ml1" v-auth="'authcert:del'" type="danger" @click="deleteRow(scope.$index)" icon="delete" link></el-button>
|
||||
|
||||
<el-button
|
||||
|
||||
@@ -274,7 +274,7 @@ const perms = {
|
||||
};
|
||||
|
||||
const searchItems = [
|
||||
getTagPathSearchItem(TagResourceTypeEnum.Machine.value),
|
||||
getTagPathSearchItem(TagResourceTypeEnum.MachineAuthCert.value),
|
||||
SearchItem.input('code', '编号'),
|
||||
SearchItem.input('ip', 'IP'),
|
||||
SearchItem.input('name', '名称'),
|
||||
|
||||
@@ -5,17 +5,16 @@
|
||||
:page-api="resourceAuthCertApi.listByQuery"
|
||||
:search-items="state.searchItems"
|
||||
v-model:query-form="query"
|
||||
:show-selection="true"
|
||||
v-model:selection-data="selectionData"
|
||||
:columns="state.columns"
|
||||
>
|
||||
<template #tableHeader>
|
||||
<el-button v-auth="'authcert:save'" type="primary" icon="plus" @click="edit(false)">添加</el-button>
|
||||
<el-button v-auth="'authcert:del'" :disabled="disabledDelBtn" @click="deleteAc(selectionData)" type="danger" icon="delete">删除 </el-button>
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button v-auth="'authcert:save'" v-if="data.type == AuthCertTypeEnum.Public.value" @click="edit(data)" type="primary" link>编辑 </el-button>
|
||||
<el-button v-auth="'authcert:save'" @click="edit(data)" type="primary" link>编辑</el-button>
|
||||
|
||||
<el-button v-auth="'authcert:del'" @click="deleteAc(data)" type="danger" link>删除</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
@@ -23,15 +22,15 @@
|
||||
v-model:visible="editor.visible"
|
||||
:auth-cert="editor.authcert"
|
||||
@confirm="confirmSave"
|
||||
:disable-type="state.disableAuthCertType"
|
||||
:disable-ciphertext-type="state.disableAuthCertCiphertextType"
|
||||
:resource-edit="false"
|
||||
:disable-ciphertext-type="[AuthCertCiphertextTypeEnum.Public.value]"
|
||||
:disable-type="[AuthCertTypeEnum.Private.value, AuthCertTypeEnum.PrivateDefault.value, AuthCertTypeEnum.Privileged.value]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive, onMounted, ref, Ref, computed } from 'vue';
|
||||
import { toRefs, reactive, onMounted, ref, Ref } from 'vue';
|
||||
import { resourceAuthCertApi } from './api';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
@@ -55,19 +54,18 @@ const state = reactive({
|
||||
],
|
||||
columns: [
|
||||
TableColumn.new('name', '名称'),
|
||||
TableColumn.new('type', '凭证类型').typeTag(AuthCertTypeEnum),
|
||||
TableColumn.new('username', '用户名'),
|
||||
TableColumn.new('ciphertextType', '密文类型').typeTag(AuthCertCiphertextTypeEnum),
|
||||
TableColumn.new('type', '凭证类型').typeTag(AuthCertTypeEnum),
|
||||
TableColumn.new('resourceType', '资源类型').typeTag(TagResourceTypeEnum),
|
||||
TableColumn.new('resourceCode', '资源编号'),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('creator', '创建人'),
|
||||
TableColumn.new('createTime', '创建时间').isTime(),
|
||||
TableColumn.new('creator', '修改者'),
|
||||
TableColumn.new('createTime', '修改时间').isTime(),
|
||||
TableColumn.new('action', '操作').isSlot().fixedRight().setMinWidth(65).alignCenter(),
|
||||
TableColumn.new('modifier', '修改者'),
|
||||
TableColumn.new('updateTime', '修改时间').isTime(),
|
||||
TableColumn.new('action', '操作').isSlot().fixedRight().setMinWidth(120).alignCenter(),
|
||||
],
|
||||
selectionData: [],
|
||||
paramsDialog: {
|
||||
visible: false,
|
||||
config: null as any,
|
||||
@@ -79,9 +77,11 @@ const state = reactive({
|
||||
visible: false,
|
||||
authcert: {},
|
||||
},
|
||||
disableAuthCertType: [] as any,
|
||||
disableAuthCertCiphertextType: [] as any,
|
||||
});
|
||||
|
||||
const { query, selectionData, editor } = toRefs(state);
|
||||
const { query, editor } = toRefs(state);
|
||||
|
||||
onMounted(() => {});
|
||||
|
||||
@@ -89,13 +89,19 @@ const search = async () => {
|
||||
pageTableRef.value.search();
|
||||
};
|
||||
|
||||
const disabledDelBtn = computed(() => {
|
||||
return state.selectionData.length < 1 || state.selectionData.find((item: any) => item.type != AuthCertTypeEnum.Public.value);
|
||||
});
|
||||
|
||||
const edit = (data: any) => {
|
||||
state.disableAuthCertType = [];
|
||||
state.disableAuthCertCiphertextType = [];
|
||||
if (data) {
|
||||
state.editor.authcert = data;
|
||||
// 如果数据为公共授权凭证,则不允许修改凭证类型
|
||||
if (data.type == AuthCertTypeEnum.Public.value) {
|
||||
state.disableAuthCertType = [AuthCertTypeEnum.Private.value, AuthCertTypeEnum.PrivateDefault.value, AuthCertTypeEnum.Privileged.value];
|
||||
state.disableAuthCertCiphertextType = [AuthCertCiphertextTypeEnum.Public.value];
|
||||
} else {
|
||||
// 如果非公共凭证,也无法修改为公共凭证
|
||||
state.disableAuthCertType = [AuthCertTypeEnum.Public.value];
|
||||
}
|
||||
} else {
|
||||
state.editor.authcert = {
|
||||
type: AuthCertTypeEnum.Public.value,
|
||||
@@ -116,12 +122,12 @@ const confirmSave = async (authCert: any) => {
|
||||
|
||||
const deleteAc = async (data: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定删除该【${data.map((x: any) => x.name).join(', ')}授权凭证?`, '提示', {
|
||||
await ElMessageBox.confirm(`确定删除该【${data.name}授权凭证?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
});
|
||||
await resourceAuthCertApi.delete.request({ id: data.map((x: any) => x.id).join(',') });
|
||||
await resourceAuthCertApi.delete.request({ id: data.id });
|
||||
ElMessage.success('删除成功');
|
||||
search();
|
||||
} catch (err) {
|
||||
|
||||
@@ -78,19 +78,31 @@
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="currentTag.type == TagResourceTypeEnum.Tag.value" :label="`机器 (${resourceCount.machine})`" :name="MachineTag">
|
||||
<el-tab-pane
|
||||
:disabled="currentTag.type != TagResourceTypeEnum.Tag.value"
|
||||
:label="`机器 (${resourceCount.machine || 0})`"
|
||||
:name="MachineTag"
|
||||
>
|
||||
<MachineList lazy ref="machineListRef" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="currentTag.type == TagResourceTypeEnum.Tag.value" :label="`数据库 (${resourceCount.db})`" :name="DbTag">
|
||||
<el-tab-pane :disabled="currentTag.type != TagResourceTypeEnum.Tag.value" :label="`数据库 (${resourceCount.db || 0})`" :name="DbTag">
|
||||
<DbList lazy ref="dbListRef" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="currentTag.type == TagResourceTypeEnum.Tag.value" :label="`Redis (${resourceCount.redis})`" :name="RedisTag">
|
||||
<el-tab-pane
|
||||
:disabled="currentTag.type != TagResourceTypeEnum.Tag.value"
|
||||
:label="`Redis (${resourceCount.redis || 0})`"
|
||||
:name="RedisTag"
|
||||
>
|
||||
<RedisList lazy ref="redisListRef" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-if="currentTag.type == TagResourceTypeEnum.Tag.value" :label="`Mongo (${resourceCount.mongo})`" :name="MongoTag">
|
||||
<el-tab-pane
|
||||
:disabled="currentTag.type != TagResourceTypeEnum.Tag.value"
|
||||
:label="`Mongo (${resourceCount.mongo || 0})`"
|
||||
:name="MongoTag"
|
||||
>
|
||||
<MongoList lazy ref="mongoListRef" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
@@ -233,9 +245,12 @@ watch(filterTag, (val) => {
|
||||
watch(
|
||||
() => state.currentTag,
|
||||
(val: any) => {
|
||||
tagApi.countTagResource.request({ tagPath: val.codePath }).then((res: any) => {
|
||||
state.resourceCount = res;
|
||||
});
|
||||
if (val.type == TagResourceTypeEnum.Tag.value) {
|
||||
tagApi.countTagResource.request({ tagPath: val.codePath }).then((res: any) => {
|
||||
state.resourceCount = res;
|
||||
});
|
||||
}
|
||||
|
||||
setNowTabData();
|
||||
}
|
||||
);
|
||||
|
||||
@@ -86,7 +86,6 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara
|
||||
tagIds := param.TagIds
|
||||
authCerts := param.AuthCerts
|
||||
resourceType := tagentity.TagTypeMachine
|
||||
authCertTagType := tagentity.TagTypeMachineAuthCert
|
||||
|
||||
oldMachine := &entity.Machine{
|
||||
Ip: me.Ip,
|
||||
@@ -119,11 +118,10 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara
|
||||
return err
|
||||
}
|
||||
|
||||
return m.resourceAuthCertApp.SaveAuthCert(ctx, &tagapp.SaveAuthCertParam{
|
||||
ResourceCode: me.Code,
|
||||
ResourceType: resourceType,
|
||||
AuthCertTagType: authCertTagType,
|
||||
AuthCerts: authCerts,
|
||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||
ResourceCode: me.Code,
|
||||
ResourceType: resourceType,
|
||||
AuthCerts: authCerts,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -152,11 +150,10 @@ func (m *machineAppImpl) SaveMachine(ctx context.Context, param *SaveMachinePara
|
||||
return err
|
||||
}
|
||||
|
||||
return m.resourceAuthCertApp.SaveAuthCert(ctx, &tagapp.SaveAuthCertParam{
|
||||
ResourceCode: oldMachine.Code,
|
||||
ResourceType: resourceType,
|
||||
AuthCertTagType: authCertTagType,
|
||||
AuthCerts: authCerts,
|
||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||
ResourceCode: oldMachine.Code,
|
||||
ResourceType: resourceType,
|
||||
AuthCerts: authCerts,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -216,7 +213,7 @@ func (m *machineAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||
ResourceType: resourceType,
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return m.resourceAuthCertApp.SaveAuthCert(ctx, &tagapp.SaveAuthCertParam{
|
||||
return m.resourceAuthCertApp.RelateAuthCert(ctx, &tagapp.RelateAuthCertParam{
|
||||
ResourceCode: machine.Code,
|
||||
ResourceType: resourceType,
|
||||
})
|
||||
|
||||
@@ -6,8 +6,6 @@ import (
|
||||
"mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
|
||||
"github.com/may-fly/cast"
|
||||
)
|
||||
@@ -51,25 +49,11 @@ func (c *ResourceAuthCert) SaveAuthCert(rc *req.Ctx) {
|
||||
acForm.Ciphertext = "***"
|
||||
rc.ReqParam = acForm
|
||||
|
||||
biz.ErrIsNil(c.ResourceAuthCertApp.SavePulbicAuthCert(rc.MetaCtx, ac))
|
||||
biz.ErrIsNil(c.ResourceAuthCertApp.SaveAuthCert(rc.MetaCtx, ac))
|
||||
}
|
||||
|
||||
func (c *ResourceAuthCert) Delete(rc *req.Ctx) {
|
||||
idsStr := rc.PathParam("id")
|
||||
ids := strings.Split(idsStr, ",")
|
||||
|
||||
acIds := make([]uint64, 0)
|
||||
acNames := make([]string, 0)
|
||||
for _, v := range ids {
|
||||
id := cast.ToUint64(v)
|
||||
rac, err := c.ResourceAuthCertApp.GetById(new(entity.ResourceAuthCert), id)
|
||||
biz.ErrIsNil(err, "存在错误授权凭证id")
|
||||
biz.IsTrue(rac.Type == entity.AuthCertTypePublic, "只允许删除公共授权凭证")
|
||||
biz.IsTrue(c.ResourceAuthCertApp.CountByCond(&entity.ResourceAuthCert{Ciphertext: rac.Name}) == 0, "[%s]该授权凭证已被关联", rac.Name)
|
||||
acIds = append(acIds, id)
|
||||
acNames = append(acNames, rac.Name)
|
||||
}
|
||||
|
||||
rc.ReqParam = acNames
|
||||
biz.ErrIsNil(c.ResourceAuthCertApp.DeleteByWheres(rc.MetaCtx, collx.M{"id in ?": acIds}))
|
||||
id := rc.PathParamInt("id")
|
||||
rc.ReqParam = id
|
||||
biz.ErrIsNil(c.ResourceAuthCertApp.DeleteAuthCert(rc.MetaCtx, cast.ToUint64(id)))
|
||||
}
|
||||
|
||||
@@ -102,8 +102,14 @@ func (p *TagTree) TagResources(rc *req.Ctx) {
|
||||
func (p *TagTree) CountTagResource(rc *req.Ctx) {
|
||||
tagPath := rc.Query("tagPath")
|
||||
accountId := rc.GetLoginAccount().Id
|
||||
|
||||
machienAuthCerts := p.ResourceAuthCertApp.GetAccountAuthCert(accountId, entity.TagTypeMachineAuthCert, tagPath)
|
||||
machineCodes := collx.ArrayMap(machienAuthCerts, func(ac *entity.ResourceAuthCert) string {
|
||||
return ac.ResourceCode
|
||||
})
|
||||
|
||||
rc.ResData = collx.M{
|
||||
"machine": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMachine, tagPath)),
|
||||
"machine": len(collx.ArrayDeduplicate(machineCodes)),
|
||||
"db": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeDb, tagPath)),
|
||||
"redis": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeRedis, tagPath)),
|
||||
"mongo": len(p.TagTreeApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMongo, tagPath)),
|
||||
|
||||
@@ -10,14 +10,12 @@ import (
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
)
|
||||
|
||||
type SaveAuthCertParam struct {
|
||||
type RelateAuthCertParam struct {
|
||||
ResourceCode string
|
||||
|
||||
// 资源标签类型
|
||||
ResourceType entity.TagType
|
||||
|
||||
// 授权凭证类型
|
||||
AuthCertTagType entity.TagType
|
||||
|
||||
// 空数组则为删除该资源绑定的授权凭证
|
||||
AuthCerts []*entity.ResourceAuthCert
|
||||
}
|
||||
@@ -25,11 +23,13 @@ type SaveAuthCertParam struct {
|
||||
type ResourceAuthCert interface {
|
||||
base.App[*entity.ResourceAuthCert]
|
||||
|
||||
// SaveAuthCert 保存资源授权凭证信息,不可放于事务中
|
||||
SaveAuthCert(ctx context.Context, param *SaveAuthCertParam) error
|
||||
// RelateAuthCert 保存资源授权凭证信息,不可放于事务中
|
||||
RelateAuthCert(ctx context.Context, param *RelateAuthCertParam) error
|
||||
|
||||
// SavePublicAuthCert 保存公共授权凭证信息
|
||||
SavePulbicAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error
|
||||
// SaveAuthCert 保存授权凭证信息
|
||||
SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error
|
||||
|
||||
DeleteAuthCert(ctx context.Context, id uint64) error
|
||||
|
||||
// GetAuthCert 根据授权凭证名称获取授权凭证
|
||||
GetAuthCert(authCertName string) (*entity.ResourceAuthCert, error)
|
||||
@@ -56,23 +56,25 @@ func (r *resourceAuthCertAppImpl) InjectResourceAuthCertRepo(resourceAuthCertRep
|
||||
r.Repo = resourceAuthCertRepo
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *SaveAuthCertParam) error {
|
||||
func (r *resourceAuthCertAppImpl) RelateAuthCert(ctx context.Context, params *RelateAuthCertParam) error {
|
||||
resourceCode := params.ResourceCode
|
||||
resourceType := int8(params.ResourceType)
|
||||
resourceAuthCerts := params.AuthCerts
|
||||
authCertTagType := params.AuthCertTagType
|
||||
authCertTagType := getResourceAuthCertTagType(entity.TagType(resourceType))
|
||||
|
||||
if authCertTagType == 0 {
|
||||
return errorx.NewBiz("资源授权凭证所属标签类型不能为空")
|
||||
}
|
||||
|
||||
if resourceCode == "" {
|
||||
return errorx.NewBiz("资源授权凭证的资源编号不能为空")
|
||||
}
|
||||
if resourceType == 0 {
|
||||
return errorx.NewBiz("资源类型不能为空")
|
||||
}
|
||||
|
||||
// 删除授权信息
|
||||
if len(resourceAuthCerts) == 0 {
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-删除所有关联的授权凭证信息", resourceType, resourceCode)
|
||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-删除所有关联的授权凭证信息", resourceType, resourceCode)
|
||||
if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -114,9 +116,10 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
|
||||
var oldAuthCert []*entity.ResourceAuthCert
|
||||
r.ListByCond(&entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType}, &oldAuthCert)
|
||||
|
||||
// 新增、删除以及不变的授权凭证名
|
||||
var adds, dels, unmodifys []string
|
||||
if len(oldAuthCert) == 0 {
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-不存在已有的授权凭证信息, 为新增资源授权凭证", resourceType, resourceCode)
|
||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-不存在已有的授权凭证信息, 为新增资源授权凭证", resourceType, resourceCode)
|
||||
adds = collx.MapKeys(name2AuthCert)
|
||||
} else {
|
||||
oldNames := collx.ArrayMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string {
|
||||
@@ -134,7 +137,7 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
|
||||
|
||||
// 处理新增的授权凭证
|
||||
if len(addAuthCerts) > 0 {
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-新增授权凭证-[%v]", resourceType, resourceCode, adds)
|
||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-新增授权凭证-[%v]", resourceType, resourceCode, adds)
|
||||
if err := r.BatchInsert(ctx, addAuthCerts); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -150,7 +153,7 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
|
||||
if len(resourceTagIds) > 0 {
|
||||
// 保存授权凭证类型的资源标签
|
||||
for _, authCert := range addAuthCerts {
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, authCert.Name, resourceTagIds)
|
||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, authCert.Name, resourceTagIds)
|
||||
if err := r.tagTreeApp.SaveResource(ctx, &SaveResourceTagParam{
|
||||
ResourceCode: authCert.Name,
|
||||
ResourceType: authCertTagType,
|
||||
@@ -164,7 +167,7 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
|
||||
}
|
||||
|
||||
for _, del := range dels {
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-删除授权凭证-[%v]", resourceType, resourceCode, del)
|
||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-删除授权凭证-[%v]", resourceType, resourceCode, del)
|
||||
if err := r.DeleteByCond(ctx, &entity.ResourceAuthCert{ResourceCode: resourceCode, ResourceType: resourceType, Name: del}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -177,39 +180,75 @@ func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, params *Save
|
||||
}
|
||||
}
|
||||
|
||||
for _, unmodify := range unmodifys {
|
||||
unmodifyAc := name2AuthCert[unmodify]
|
||||
if unmodifyAc.Id == 0 {
|
||||
continue
|
||||
}
|
||||
logx.DebugfContext(ctx, "SaveAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodify)
|
||||
// 置空用户名,不允许修改(TagTree的name关联了该值,修改了会造成数据不一致,懒得处理该同步。要修改用户名可通过重新删除旧凭证后添加一个授权凭证或通过关联公共凭证(公共凭证可修改用户名))
|
||||
if unmodifyAc.CiphertextType != entity.AuthCertCiphertextTypePublic {
|
||||
unmodifyAc.Username = ""
|
||||
}
|
||||
if len(unmodifys) > 0 {
|
||||
// 旧凭证名 -> 旧凭证
|
||||
oldName2AuthCert := collx.ArrayToMap(oldAuthCert, func(ac *entity.ResourceAuthCert) string {
|
||||
return ac.Name
|
||||
})
|
||||
for _, unmodify := range unmodifys {
|
||||
unmodifyAc := name2AuthCert[unmodify]
|
||||
if unmodifyAc.Id == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := r.UpdateById(ctx, unmodifyAc); err != nil {
|
||||
return err
|
||||
oldAuthCert := oldName2AuthCert[unmodify]
|
||||
if !unmodifyAc.HasChanged(oldAuthCert) {
|
||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-授权凭证[%s]未发生字段变更", resourceType, resourceCode, unmodify)
|
||||
continue
|
||||
}
|
||||
|
||||
logx.DebugfContext(ctx, "RelateAuthCert[%d-%s]-更新授权凭证-[%v]", resourceType, resourceCode, unmodify)
|
||||
if oldAuthCert.Username != unmodifyAc.Username {
|
||||
if err := r.updateAuthCertTagName(ctx, unmodify, authCertTagType, unmodifyAc.Username); err != nil {
|
||||
logx.WarnfContext(ctx, "授权凭证[%s]修改了用户名-同步更新授权凭证标签名失败", unmodify)
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.UpdateById(ctx, unmodifyAc); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) SavePulbicAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
|
||||
rac.Type = entity.AuthCertTypePublic
|
||||
rac.CiphertextEncrypt()
|
||||
rac.ResourceType = 0
|
||||
rac.ResourceCode = "-"
|
||||
func (r *resourceAuthCertAppImpl) SaveAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
|
||||
if rac.Id == 0 {
|
||||
if r.CountByCond(&entity.ResourceAuthCert{Name: rac.Name}) > 0 {
|
||||
return errorx.NewBiz("授权凭证的名称不能重复[%s]", rac.Name)
|
||||
}
|
||||
return r.Insert(ctx, rac)
|
||||
return r.addAuthCert(ctx, rac)
|
||||
}
|
||||
// 名称置空,防止被更新
|
||||
rac.Name = ""
|
||||
return r.UpdateById(ctx, rac)
|
||||
|
||||
return r.updateAuthCert(ctx, rac)
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) DeleteAuthCert(ctx context.Context, id uint64) error {
|
||||
rac, err := r.GetById(new(entity.ResourceAuthCert), id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("授权凭证不存在")
|
||||
}
|
||||
|
||||
if rac.Type == entity.AuthCertTypePublic {
|
||||
if r.CountByCond(&entity.ResourceAuthCert{Ciphertext: rac.Name}) > 0 {
|
||||
return errorx.NewBiz("该公共授权凭证[%s]已被关联", rac.Name)
|
||||
}
|
||||
// 公共授权凭证直接删除即可
|
||||
return r.DeleteById(ctx, id)
|
||||
}
|
||||
|
||||
if r.CountByCond(&entity.ResourceAuthCert{ResourceCode: rac.ResourceCode, ResourceType: rac.ResourceType}) <= 1 {
|
||||
return errorx.NewBiz("资源至少需要绑定一个授权凭证,无法删除该凭证[%s]", rac.Name)
|
||||
}
|
||||
|
||||
return r.Tx(ctx,
|
||||
func(ctx context.Context) error {
|
||||
// 删除对应授权凭证标签
|
||||
return r.tagTreeApp.DeleteResource(ctx, &DelResourceTagParam{
|
||||
ResourceCode: rac.Name,
|
||||
})
|
||||
},
|
||||
func(ctx context.Context) error {
|
||||
return r.DeleteById(ctx, id)
|
||||
})
|
||||
}
|
||||
|
||||
func (r *resourceAuthCertAppImpl) GetAuthCert(authCertName string) (*entity.ResourceAuthCert, error) {
|
||||
@@ -290,6 +329,109 @@ func (r *resourceAuthCertAppImpl) FillAuthCert(authCerts []*entity.ResourceAuthC
|
||||
}
|
||||
}
|
||||
|
||||
// addAuthCert 添加授权凭证
|
||||
func (r *resourceAuthCertAppImpl) addAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
|
||||
if r.CountByCond(&entity.ResourceAuthCert{Name: rac.Name}) > 0 {
|
||||
return errorx.NewBiz("授权凭证的名称不能重复[%s]", rac.Name)
|
||||
}
|
||||
// 公共凭证
|
||||
if rac.Type == entity.AuthCertTypePublic {
|
||||
rac.ResourceCode = "-"
|
||||
rac.CiphertextEncrypt()
|
||||
rac.Type = -2
|
||||
return r.Insert(ctx, rac)
|
||||
}
|
||||
|
||||
resourceCode := rac.ResourceCode
|
||||
resourceType := rac.ResourceType
|
||||
|
||||
// 获取资源编号对应的资源标签信息
|
||||
var resourceTags []*entity.TagTree
|
||||
r.tagTreeApp.ListByCond(&entity.TagTree{Type: entity.TagType(resourceType), Code: resourceCode}, &resourceTags)
|
||||
// 资源标签id(相当于父tag id)
|
||||
resourceTagIds := collx.ArrayMap(resourceTags, func(tag *entity.TagTree) uint64 {
|
||||
return tag.Id
|
||||
})
|
||||
if len(resourceTagIds) == 0 {
|
||||
return errorx.NewBiz("资源标签不存在[%s], 请检查资源编号是否正确", resourceCode)
|
||||
}
|
||||
|
||||
authCertTagType := getResourceAuthCertTagType(entity.TagType(resourceType))
|
||||
// 如果密文类型不为公共凭证,则进行加密。公共凭证密文内容存的是明文的公共凭证名
|
||||
if rac.CiphertextType != entity.AuthCertCiphertextTypePublic {
|
||||
rac.CiphertextEncrypt()
|
||||
}
|
||||
|
||||
return r.Tx(ctx, func(ctx context.Context) error {
|
||||
logx.DebugfContext(ctx, "[%d-%s]-授权凭证标签[%d-%s]关联至所属资源标签下[%v]", resourceType, resourceCode, authCertTagType, rac.Name, resourceTagIds)
|
||||
return r.tagTreeApp.SaveResource(ctx, &SaveResourceTagParam{
|
||||
ResourceCode: rac.Name,
|
||||
ResourceType: authCertTagType,
|
||||
ResourceName: rac.Username,
|
||||
TagIds: resourceTagIds,
|
||||
})
|
||||
}, func(ctx context.Context) error {
|
||||
return r.Insert(ctx, rac)
|
||||
})
|
||||
}
|
||||
|
||||
// updateAuthCert 更新授权凭证
|
||||
func (r *resourceAuthCertAppImpl) updateAuthCert(ctx context.Context, rac *entity.ResourceAuthCert) error {
|
||||
oldRac, err := r.GetById(new(entity.ResourceAuthCert), rac.Id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("该授权凭证不存在")
|
||||
}
|
||||
|
||||
if !oldRac.HasChanged(rac) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if oldRac.Type == entity.AuthCertTypePublic {
|
||||
// 如果旧凭证为公共凭证,则不允许修改凭证类型
|
||||
if rac.Type != entity.AuthCertTypePublic {
|
||||
return errorx.NewBiz("公共授权凭证不允许修改凭证类型")
|
||||
}
|
||||
|
||||
if rac.CiphertextType == entity.AuthCertCiphertextTypePublic {
|
||||
return errorx.NewBiz("公共授权凭证不允许绑定其他公共授权凭证")
|
||||
}
|
||||
} else {
|
||||
if rac.Type == entity.AuthCertTypePublic {
|
||||
return errorx.NewBiz("非公共授权凭证不允许修改为公共凭证")
|
||||
}
|
||||
|
||||
// 修改了用户名,则需要同步更新对应授权凭证标签里的名称
|
||||
if rac.Username != oldRac.Username {
|
||||
if err := r.updateAuthCertTagName(ctx, oldRac.Name, getResourceAuthCertTagType(entity.TagType(oldRac.ResourceType)), rac.Username); err != nil {
|
||||
return errorx.NewBiz("同步更新授权凭证标签名称失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 防止误更新
|
||||
rac.Name = ""
|
||||
rac.ResourceCode = ""
|
||||
rac.ResourceType = 0
|
||||
return r.UpdateById(ctx, rac)
|
||||
}
|
||||
|
||||
// updateAuthCertTagName 同步更新授权凭证的标签名,防止标签展示不一致
|
||||
func (r *resourceAuthCertAppImpl) updateAuthCertTagName(ctx context.Context, authCertName string, autyCertTagType entity.TagType, newTagName string) error {
|
||||
return r.tagTreeApp.UpdateByWheres(ctx, &entity.TagTree{Name: newTagName}, collx.M{"code = ?": authCertName, "type = ?": autyCertTagType})
|
||||
}
|
||||
|
||||
// getResourceAuthCertTagType 根据资源类型,获取对应的授权凭证标签类型
|
||||
func getResourceAuthCertTagType(resourceType entity.TagType) entity.TagType {
|
||||
if resourceType == entity.TagTypeMachine {
|
||||
return entity.TagTypeMachineAuthCert
|
||||
}
|
||||
if resourceType == entity.TagTypeDb {
|
||||
return entity.TagTypeDbAuthCert
|
||||
}
|
||||
|
||||
return -2
|
||||
}
|
||||
|
||||
// 解密授权凭证信息
|
||||
func (r *resourceAuthCertAppImpl) decryptAuthCert(authCert *entity.ResourceAuthCert) (*entity.ResourceAuthCert, error) {
|
||||
if authCert.CiphertextType == entity.AuthCertCiphertextTypePublic {
|
||||
|
||||
@@ -152,19 +152,30 @@ func (p *tagTreeAppImpl) GetAccountTagResources(accountId uint64, query *entity.
|
||||
if len(accountTagPaths) == 0 {
|
||||
accountTagPaths = tagPaths
|
||||
} else {
|
||||
accountTagPaths = collx.ArrayFilter[string](tagPaths, func(s string) bool {
|
||||
for _, v := range accountTagPaths {
|
||||
// 要过滤的权限需要在用户拥有的子标签下, accountTagPath: test/ tagPath: test/test1/ -> true
|
||||
if strings.HasPrefix(v, s) {
|
||||
queryPaths := collx.ArrayFilter[string](tagPaths, func(tagPath string) bool {
|
||||
for _, acPath := range accountTagPaths {
|
||||
// 查询条件: a/b/ 有权的:a/ 查询结果应该是 a/b/
|
||||
if strings.HasPrefix(tagPath, acPath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
acPaths := collx.ArrayFilter[string](accountTagPaths, func(acPath string) bool {
|
||||
for _, tagPath := range tagPaths {
|
||||
// 查询条件: a/ 有权的:a/b/ 查询结果应该是 a/b/
|
||||
if strings.HasPrefix(acPath, tagPath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
accountTagPaths = append(queryPaths, acPaths...)
|
||||
}
|
||||
}
|
||||
|
||||
// tagResourceQuery.CodePathLike = tagPath
|
||||
tagResourceQuery.Codes = query.Codes
|
||||
tagResourceQuery.CodePathLikes = accountTagPaths
|
||||
p.ListByQuery(tagResourceQuery, &tagResources)
|
||||
|
||||
@@ -97,6 +97,19 @@ func (m *ResourceAuthCert) GetExtraString(key string) string {
|
||||
return cast.ToString(m.Extra[key])
|
||||
}
|
||||
|
||||
// HasChanged 与指定授权凭证比较是否有变更
|
||||
func (m *ResourceAuthCert) HasChanged(rac *ResourceAuthCert) bool {
|
||||
if rac == nil {
|
||||
return true
|
||||
}
|
||||
return m.Username != rac.Username ||
|
||||
m.Ciphertext != rac.Ciphertext ||
|
||||
m.CiphertextType != rac.CiphertextType ||
|
||||
m.Remark != rac.Remark ||
|
||||
m.Type != rac.Type ||
|
||||
m.GetExtraString(ExtraKeyPassphrase) != rac.GetExtraString(ExtraKeyPassphrase)
|
||||
}
|
||||
|
||||
// 密文类型
|
||||
type AuthCertCiphertextType int8
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ const (
|
||||
TagTypeRedis TagType = TagType(consts.TagResourceTypeRedis)
|
||||
TagTypeMongo TagType = TagType(consts.TagResourceTypeMongo)
|
||||
|
||||
// ----- (单独声明各个资源的授权凭证类型而不统一使用一个授权凭证类型是为了获取登录账号的授权凭证标签(ResourceAuthCertApp.GetAccountAuthCert)时,避免查出所有资源的授权凭证)
|
||||
|
||||
TagTypeMachineAuthCert TagType = 11 // 机器-授权凭证
|
||||
TagTypeDbAuthCert TagType = 21 // DB-授权凭证
|
||||
)
|
||||
|
||||
22
server/pkg/utils/structx/diff.go
Normal file
22
server/pkg/utils/structx/diff.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package structx
|
||||
|
||||
import "reflect"
|
||||
|
||||
// DiffFields 比较两个结构体中指定字段的属性值是否发生改变,并返回改变的字段名
|
||||
func DiffFields[T any](t1 T, t2 T, fieldsToCompare ...string) []string {
|
||||
var changedFields []string
|
||||
|
||||
oldValue := reflect.ValueOf(t1)
|
||||
newValue := reflect.ValueOf(t2)
|
||||
|
||||
for _, fieldName := range fieldsToCompare {
|
||||
oldFieldValue := oldValue.FieldByName(fieldName)
|
||||
newFieldValue := newValue.FieldByName(fieldName)
|
||||
|
||||
if !reflect.DeepEqual(oldFieldValue.Interface(), newFieldValue.Interface()) {
|
||||
changedFields = append(changedFields, fieldName)
|
||||
}
|
||||
}
|
||||
|
||||
return changedFields
|
||||
}
|
||||
Reference in New Issue
Block a user