feat: 资产授权.

This commit is contained in:
lijiahangmax
2023-12-04 21:25:31 +08:00
parent 0ecbd605e9
commit 798b0c61ee
20 changed files with 319 additions and 168 deletions

View File

@@ -8,6 +8,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 主机 缓存对象
@@ -35,4 +36,10 @@ public class HostCacheDTO implements LongCacheIdModel, Serializable {
@Schema(description = "主机地址")
private String address;
@Schema(description = "创建时间")
private Date createTime;
@Schema(description = "修改时间")
private Date updateTime;
}

View File

@@ -8,6 +8,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 主机身份缓存
@@ -32,4 +33,13 @@ public class HostIdentityCacheDTO implements LongCacheIdModel, Serializable {
@Schema(description = "用户名")
private String username;
@Schema(description = "秘钥id")
private Long keyId;
@Schema(description = "创建时间")
private Date createTime;
@Schema(description = "修改时间")
private Date updateTime;
}

View File

@@ -8,6 +8,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* 主机秘钥缓存
@@ -29,4 +30,10 @@ public class HostKeyCacheDTO implements LongCacheIdModel, Serializable {
@Schema(description = "名称")
private String name;
@Schema(description = "创建时间")
private Date createTime;
@Schema(description = "修改时间")
private Date updateTime;
}

View File

@@ -35,7 +35,7 @@ export function getAuthorizedHostGroup(params: AssetAuthorizedDataQueryRequest)
* 主机秘钥授权
*/
export function grantHostKey(request: AssetDataGrantRequest) {
return axios.put('/asset/host-group/grant-host-key', request);
return axios.put('/asset/data-grant/grant-host-key', request);
}
/**
@@ -49,7 +49,7 @@ export function getAuthorizedHostKey(params: AssetAuthorizedDataQueryRequest) {
* 主机身份授权
*/
export function grantHostIdentity(request: AssetDataGrantRequest) {
return axios.put('/asset/host-group/grant-host-identity', request);
return axios.put('/asset/data-grant/grant-host-identity', request);
}
/**

View File

@@ -73,6 +73,14 @@ body {
display: none;
}
.sticky-list {
.arco-list-header {
position: sticky;
background: var(--color-bg-2);
top: 0;
}
}
.arco-table-td-content {
color: rgba(var(--gray-9), .95);
font-size: 13px;

View File

@@ -41,7 +41,7 @@
title: String,
options: {
type: Array as PropType<OptionsProps[]>,
default: []
default: () => []
},
});

View File

@@ -49,7 +49,7 @@
},
options: {
type: Array as PropType<Array<RadioOption | SelectOption>>,
default: []
default: () => []
}
});
const emit = defineEmits(['inputChange']);

View File

@@ -68,7 +68,9 @@
const props = defineProps({
itemData: {
type: Object as PropType<TagProps>,
default: [],
default: () => {
return {};
}
},
index: {
type: Number,

View File

@@ -108,7 +108,7 @@
},
checkedKeys: {
type: Array<Number>,
default: []
default: () => []
}
});
const emits = defineEmits(['loading', 'selectNode', 'update:checkedKeys']);

View File

@@ -37,8 +37,7 @@
@select-node="e => selectedGroup = e"
@update:checked-keys="updateCheckedGroups" />
<!-- 主机列表 -->
<host-list class="group-main-hosts"
:group="selectedGroup" />
<host-list class="group-main-hosts sticky-list" :group="selectedGroup" />
</div>
</div>
</a-spin>
@@ -57,9 +56,9 @@
import { getAuthorizedHostGroup, grantHostGroup } from '@/api/asset/asset-data-grant';
import { AdminRoleCode } from '@/types/const';
import { Message } from '@arco-design/web-vue';
import HostGroupTree from '@/components/asset/host-group/host-group-tree.vue';
import HostList from './host-list.vue';
import RouterRoles from './router-roles.vue';
import HostGroupTree from '@/components/asset/host-group/host-group-tree.vue';
const { loading, setLoading } = useLoading();

View File

@@ -28,7 +28,7 @@
<!-- 主题部分 -->
<div class="group-main">
<!-- 分组 -->
<host-group-tree outer-class="group-main-tree"
<host-group-tree outer-class="group-main-tree sticky-list"
:checked-keys="checkedGroups"
:editable="false"
:loading="loading"
@@ -36,8 +36,7 @@
@select-node="e => selectedGroup = e"
@update:checked-keys="updateCheckedGroups" />
<!-- 主机列表 -->
<host-list class="group-main-hosts"
:group="selectedGroup" />
<host-list class="group-main-hosts" :group="selectedGroup" />
</div>
</div>
</a-spin>
@@ -52,12 +51,12 @@
<script lang="ts" setup>
import type { TreeNodeData } from '@arco-design/web-vue';
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import { getAuthorizedHostGroup, grantHostGroup } from '@/api/asset/asset-data-grant';
import { Message } from '@arco-design/web-vue';
import HostGroupTree from '@/components/asset/host-group/host-group-tree.vue';
import useLoading from '@/hooks/loading';
import HostList from './host-list.vue';
import RouterUsers from './router-users.vue';
import HostGroupTree from '@/components/asset/host-group/host-group-tree.vue';
const { loading, setLoading } = useLoading();

View File

@@ -0,0 +1,77 @@
<template>
<a-table row-key="id"
class="host-identity-main-table"
label-align="left"
:columns="hostIdentityColumns"
v-model:selected-keys="selectedKeys"
:row-selection="rowSelection"
:sticky-header="true"
:data="hostIdentities"
:pagination="false"
:bordered="false">
<!-- 秘钥名称 -->
<template #keyId="{ record }">
<a-tag color="arcoblue" v-if="record.keyName">{{ record.keyName }}</a-tag>
</template>
</a-table>
</template>
<script lang="ts">
export default {
name: 'host-identity-grant-table'
};
</script>
<script lang="ts" setup>
import type { PropType } from 'vue';
import type { HostIdentityQueryResponse } from '@/api/asset/host-identity';
import { hostIdentityColumns } from '../types/table.columns';
import { useRowSelection } from '@/types/table';
import { computed, ref, onMounted } from 'vue';
import { useCacheStore } from '@/store';
const props = defineProps({
modelValue: {
type: Array as PropType<Array<number>>,
default: () => []
}
});
const emits = defineEmits(['loading', 'update:modelValue']);
const cacheStore = useCacheStore();
const rowSelection = useRowSelection();
const hostIdentities = ref<Array<HostIdentityQueryResponse>>([]);
const selectedKeys = computed({
get() {
return props.modelValue;
},
set(e) {
emits('update:modelValue', e);
}
});
// 初始化数据
onMounted(async () => {
emits('loading', true);
try {
const keys = await cacheStore.loadHostKeys();
const identities = await cacheStore.loadHostIdentities();
hostIdentities.value = identities.map(s => {
return {
...s,
keyName: s.keyId ? keys.find(k => k.id === s.keyId)?.name : undefined
};
});
} catch (e) {
} finally {
emits('loading', false);
}
});
</script>
<style lang="less" scoped>
</style>

View File

@@ -3,11 +3,11 @@
<!-- 角色列表 -->
<router-roles outer-class="roles-router-wrapper"
v-model="roleId"
@change="fetchAuthorizedGroup" />
@change="fetchAuthorizedHostIdentity" />
<!-- 分组列表 -->
<div class="group-container">
<div class="host-identity-container">
<!-- 顶部 -->
<div class="group-header">
<div class="host-identity-header">
<!-- 提示信息 -->
<a-alert class="alert-wrapper" :show-icon="false">
<span v-if="currentRole" class="alert-message">
@@ -26,19 +26,9 @@
</a-button>
</div>
<!-- 主体部分 -->
<div class="group-main">
<!-- 分组 -->
<host-group-tree outer-class="group-main-tree"
:checkable="true"
:checked-keys="checkedGroups"
:editable="false"
:loading="loading"
@loading="setLoading"
@select-node="e => selectedGroup = e"
@update:checked-keys="updateCheckedGroups" />
<!-- 主机列表 -->
<host-list class="group-main-hosts"
:group="selectedGroup" />
<div class="host-identity-main">
<host-identity-grant-table v-model="selectedIdentities"
@loading="setLoading" />
</div>
</div>
</a-spin>
@@ -51,53 +41,45 @@
</script>
<script lang="ts" setup>
import type { TreeNodeData } from '@arco-design/web-vue';
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import { getAuthorizedHostGroup, grantHostGroup } from '@/api/asset/asset-data-grant';
import { getAuthorizedHostIdentity, grantHostIdentity } from '@/api/asset/asset-data-grant';
import { AdminRoleCode } from '@/types/const';
import { Message } from '@arco-design/web-vue';
import HostGroupTree from '@/components/asset/host-group/host-group-tree.vue';
import HostList from './host-list.vue';
import useLoading from '@/hooks/loading';
import { useCacheStore } from '@/store';
import RouterRoles from './router-roles.vue';
import HostIdentityGrantTable from './host-identity-grant-table.vue';
const cacheStore = useCacheStore();
const { loading, setLoading } = useLoading();
const roleId = ref();
const currentRole = ref();
const authorizedGroups = ref<Array<number>>([]);
const checkedGroups = ref<Array<number>>([]);
const selectedGroup = ref<TreeNodeData>({});
const selectedIdentities = ref<Array<number>>([]);
// 获取授权列表
const fetchAuthorizedGroup = async (id: number, role: any) => {
const fetchAuthorizedHostIdentity = async (id: number, role: any) => {
roleId.value = id;
currentRole.value = role;
setLoading(true);
try {
const { data } = await getAuthorizedHostGroup({
const { data } = await getAuthorizedHostIdentity({
roleId: roleId.value
});
authorizedGroups.value = data;
checkedGroups.value = data;
selectedIdentities.value = data;
} catch (e) {
} finally {
setLoading(false);
}
};
// 选择分组
const updateCheckedGroups = (e: Array<number>) => {
checkedGroups.value = e;
};
// 授权
const doGrant = async () => {
setLoading(true);
try {
await grantHostGroup({
await grantHostIdentity({
roleId: roleId.value,
idList: checkedGroups.value
idList: selectedIdentities.value
});
Message.success('授权成功');
} catch (e) {
@@ -121,12 +103,12 @@
border-right: 1px var(--color-neutral-3) solid;
}
.group-container {
.host-identity-container {
position: relative;
width: 100%;
height: 100%;
.group-header {
.host-identity-header {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
@@ -146,20 +128,15 @@
}
}
.group-main {
.host-identity-main {
display: flex;
position: absolute;
width: 100%;
height: calc(100% - 48px);
&-tree {
width: calc(60% - 16px);
&-table {
width: 100%;
height: 100%;
margin-right: 16px;
}
&-hosts {
width: 40%;
}
}
}

View File

@@ -3,16 +3,16 @@
<!-- 用户列表 -->
<router-users outer-class="users-router-wrapper"
v-model="userId"
@change="fetchAuthorizedGroup" />
@change="fetchAuthorizedHostIdentity" />
<!-- 分组列表 -->
<div class="group-container">
<div class="host-identity-container">
<!-- 顶部 -->
<div class="group-header">
<div class="host-identity-header">
<!-- 提示信息 -->
<a-alert class="alert-wrapper" :show-icon="false">
<span v-if="currentUser" class="alert-message">
当前选择的用户为 <span class="span-blue mr4">{{ currentUser?.text }}</span>
<span class="ml4">若当前选择的用户角色包含管理员则无需配置 (管理员拥有全部权限)</span>
<span class="ml4">若当前选择的用户用户包含管理员则无需配置 (管理员拥有全部权限)</span>
</span>
</a-alert>
<!-- 授权 -->
@@ -25,19 +25,10 @@
</template>
</a-button>
</div>
<!-- 部分 -->
<div class="group-main">
<!-- 分组 -->
<host-group-tree outer-class="group-main-tree"
:checked-keys="checkedGroups"
:editable="false"
:loading="loading"
@loading="setLoading"
@select-node="e => selectedGroup = e"
@update:checked-keys="updateCheckedGroups" />
<!-- 主机列表 -->
<host-list class="group-main-hosts"
:group="selectedGroup" />
<!-- 部分 -->
<div class="host-identity-main">
<host-identity-grant-table v-model="selectedIdentities"
@loading="setLoading" />
</div>
</div>
</a-spin>
@@ -50,52 +41,44 @@
</script>
<script lang="ts" setup>
import type { TreeNodeData } from '@arco-design/web-vue';
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import { getAuthorizedHostGroup, grantHostGroup } from '@/api/asset/asset-data-grant';
import { getAuthorizedHostIdentity, grantHostIdentity } from '@/api/asset/asset-data-grant';
import { Message } from '@arco-design/web-vue';
import HostGroupTree from '@/components/asset/host-group/host-group-tree.vue';
import HostList from './host-list.vue';
import useLoading from '@/hooks/loading';
import { useCacheStore } from '@/store';
import RouterUsers from './router-users.vue';
import HostIdentityGrantTable from '@/views/asset/grant/components/host-identity-grant-table.vue';
const cacheStore = useCacheStore();
const { loading, setLoading } = useLoading();
const userId = ref();
const currentUser = ref();
const authorizedGroups = ref<Array<number>>([]);
const checkedGroups = ref<Array<number>>([]);
const selectedGroup = ref<TreeNodeData>({});
const selectedIdentities = ref<Array<number>>([]);
// 获取授权列表
const fetchAuthorizedGroup = async (id: number, user: any) => {
const fetchAuthorizedHostIdentity = async (id: number, user: any) => {
userId.value = id;
currentUser.value = user;
setLoading(true);
try {
const { data } = await getAuthorizedHostGroup({
const { data } = await getAuthorizedHostIdentity({
userId: userId.value
});
authorizedGroups.value = data;
checkedGroups.value = data;
selectedIdentities.value = data;
} catch (e) {
} finally {
setLoading(false);
}
};
// 选择分组
const updateCheckedGroups = (e: Array<number>) => {
checkedGroups.value = e;
};
// 授权
const doGrant = async () => {
setLoading(true);
try {
await grantHostGroup({
await grantHostIdentity({
userId: userId.value,
idList: checkedGroups.value
idList: selectedIdentities.value
});
Message.success('授权成功');
} catch (e) {
@@ -119,12 +102,12 @@
border-right: 1px var(--color-neutral-3) solid;
}
.group-container {
.host-identity-container {
position: relative;
width: 100%;
height: 100%;
.group-header {
.host-identity-header {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
@@ -144,20 +127,15 @@
}
}
.group-main {
.host-identity-main {
display: flex;
position: absolute;
width: 100%;
height: calc(100% - 48px);
&-tree {
width: calc(60% - 16px);
&-table {
width: 100%;
height: 100%;
margin-right: 16px;
}
&-hosts {
width: 40%;
}
}
}

View File

@@ -0,0 +1,65 @@
<template>
<a-table row-key="id"
class="host-key-main-table"
label-align="left"
:columns="hostKeyColumns"
v-model:selected-keys="selectedKeys"
:row-selection="rowSelection"
:sticky-header="true"
:data="hostKeys"
:pagination="false"
:bordered="false" />
</template>
<script lang="ts">
export default {
name: 'host-key-grant-table'
};
</script>
<script lang="ts" setup>
import type { PropType } from 'vue';
import type { HostKeyQueryResponse } from '@/api/asset/host-key';
import { hostKeyColumns } from '../types/table.columns';
import { useRowSelection } from '@/types/table';
import { computed, ref, onMounted } from 'vue';
import { useCacheStore } from '@/store';
const props = defineProps({
modelValue: {
type: Array as PropType<Array<number>>,
default: () => []
}
});
const emits = defineEmits(['loading', 'update:modelValue']);
const cacheStore = useCacheStore();
const rowSelection = useRowSelection();
const hostKeys = ref<Array<HostKeyQueryResponse>>([]);
const selectedKeys = computed({
get() {
return props.modelValue;
},
set(e) {
emits('update:modelValue', e);
}
});
// 初始化数据
onMounted(async () => {
emits('loading', true);
try {
hostKeys.value = await cacheStore.loadHostKeys();
} catch (e) {
} finally {
emits('loading', false);
}
});
</script>
<style lang="less" scoped>
</style>

View File

@@ -27,17 +27,8 @@
</div>
<!-- 主体部分 -->
<div class="host-key-main">
<a-table row-key="id"
class="host-key-main-table"
label-align="left"
:loading="loading"
:columns="hostKeyColumns"
v-model:selected-keys="selectedKeys"
:row-selection="rowSelection"
:sticky-header="true"
:data="hostKeys"
:pagination="false"
:bordered="false" />
<host-key-grant-table v-model="selectedKeys"
@loading="setLoading" />
</div>
</div>
</a-spin>
@@ -55,13 +46,11 @@
import { getAuthorizedHostKey, grantHostKey } from '@/api/asset/asset-data-grant';
import { AdminRoleCode } from '@/types/const';
import { Message } from '@arco-design/web-vue';
import { hostKeyColumns } from '../types/table.columns';
import useLoading from '@/hooks/loading';
import { useRowSelection } from '@/types/table';
import { useCacheStore } from '@/store';
import RouterRoles from './router-roles.vue';
import HostKeyGrantTable from './host-key-grant-table.vue';
const rowSelection = useRowSelection();
const cacheStore = useCacheStore();
const { loading, setLoading } = useLoading();

View File

@@ -3,16 +3,16 @@
<!-- 用户列表 -->
<router-users outer-class="users-router-wrapper"
v-model="userId"
@change="fetchAuthorizedGroup" />
@change="fetchAuthorizedHostKey" />
<!-- 分组列表 -->
<div class="group-container">
<div class="host-key-container">
<!-- 顶部 -->
<div class="group-header">
<div class="host-key-header">
<!-- 提示信息 -->
<a-alert class="alert-wrapper" :show-icon="false">
<span v-if="currentUser" class="alert-message">
当前选择的用户为 <span class="span-blue mr4">{{ currentUser?.text }}</span>
<span class="ml4">若当前选择的用户角色包含管理员则无需配置 (管理员拥有全部权限)</span>
<span class="ml4">若当前选择的用户用户包含管理员则无需配置 (管理员拥有全部权限)</span>
</span>
</a-alert>
<!-- 授权 -->
@@ -25,19 +25,10 @@
</template>
</a-button>
</div>
<!-- 部分 -->
<div class="group-main">
<!-- 分组 -->
<host-group-tree outer-class="group-main-tree"
:checked-keys="checkedGroups"
:editable="false"
:loading="loading"
@loading="setLoading"
@select-node="e => selectedGroup = e"
@update:checked-keys="updateCheckedGroups" />
<!-- 主机列表 -->
<host-list class="group-main-hosts"
:group="selectedGroup" />
<!-- 部分 -->
<div class="host-key-main">
<host-key-grant-table v-model="selectedKeys"
@loading="setLoading" />
</div>
</div>
</a-spin>
@@ -50,52 +41,46 @@
</script>
<script lang="ts" setup>
import type { TreeNodeData } from '@arco-design/web-vue';
import { ref } from 'vue';
import useLoading from '@/hooks/loading';
import { getAuthorizedHostGroup, grantHostGroup } from '@/api/asset/asset-data-grant';
import type { HostKeyQueryResponse } from '@/api/asset/host-key';
import { onMounted, ref } from 'vue';
import { getAuthorizedHostKey, grantHostKey } from '@/api/asset/asset-data-grant';
import { Message } from '@arco-design/web-vue';
import HostGroupTree from '@/components/asset/host-group/host-group-tree.vue';
import HostList from './host-list.vue';
import useLoading from '@/hooks/loading';
import { useCacheStore } from '@/store';
import RouterUsers from './router-users.vue';
import HostKeyGrantTable from './host-key-grant-table.vue';
const cacheStore = useCacheStore();
const { loading, setLoading } = useLoading();
const userId = ref();
const currentUser = ref();
const authorizedGroups = ref<Array<number>>([]);
const checkedGroups = ref<Array<number>>([]);
const selectedGroup = ref<TreeNodeData>({});
const hostKeys = ref<Array<HostKeyQueryResponse>>([]);
const selectedKeys = ref<Array<number>>([]);
// 获取授权列表
const fetchAuthorizedGroup = async (id: number, user: any) => {
const fetchAuthorizedHostKey = async (id: number, user: any) => {
userId.value = id;
currentUser.value = user;
setLoading(true);
try {
const { data } = await getAuthorizedHostGroup({
const { data } = await getAuthorizedHostKey({
userId: userId.value
});
authorizedGroups.value = data;
checkedGroups.value = data;
selectedKeys.value = data;
} catch (e) {
} finally {
setLoading(false);
}
};
// 选择分组
const updateCheckedGroups = (e: Array<number>) => {
checkedGroups.value = e;
};
// 授权
const doGrant = async () => {
setLoading(true);
try {
await grantHostGroup({
await grantHostKey({
userId: userId.value,
idList: checkedGroups.value
idList: selectedKeys.value
});
Message.success('授权成功');
} catch (e) {
@@ -104,6 +89,17 @@
}
};
// 初始化数据
onMounted(async () => {
setLoading(true);
try {
hostKeys.value = await cacheStore.loadHostKeys();
} catch (e) {
} finally {
setLoading(false);
}
});
</script>
<style lang="less" scoped>
@@ -119,12 +115,12 @@
border-right: 1px var(--color-neutral-3) solid;
}
.group-container {
.host-key-container {
position: relative;
width: 100%;
height: 100%;
.group-header {
.host-key-header {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
@@ -144,20 +140,15 @@
}
}
.group-main {
.host-key-main {
display: flex;
position: absolute;
width: 100%;
height: calc(100% - 48px);
&-tree {
width: calc(60% - 16px);
&-table {
width: 100%;
height: 100%;
margin-right: 16px;
}
&-hosts {
width: 40%;
}
}
}

View File

@@ -37,7 +37,7 @@
// 卸载时清除 cache
onUnmounted(() => {
cacheStore.reset('users', 'roles', 'hosts', 'hostGroups');
cacheStore.reset('users', 'roles', 'hosts', 'hostGroups', 'hostKeys', 'hostIdentities');
});
// 跳转到指定页

View File

@@ -32,3 +32,45 @@ export const hostKeyColumns = [
},
},
] as TableColumnData[];
// 主机身份列
export const hostIdentityColumns = [
{
title: 'id',
dataIndex: 'id',
slotName: 'id',
width: 70,
align: 'left',
fixed: 'left',
}, {
title: '名称',
dataIndex: 'name',
slotName: 'name',
}, {
title: '用户名',
dataIndex: 'username',
slotName: 'username',
}, {
title: '主机秘钥',
dataIndex: 'keyId',
slotName: 'keyId',
}, {
title: '创建时间',
dataIndex: 'createTime',
slotName: 'createTime',
align: 'center',
width: 180,
render: ({ record }) => {
return dateFormat(new Date(record.createTime));
},
}, {
title: '修改时间',
dataIndex: 'updateTime',
slotName: 'updateTime',
align: 'center',
width: 180,
render: ({ record }) => {
return dateFormat(new Date(record.updateTime));
},
},
] as TableColumnData[];

View File

@@ -57,7 +57,7 @@
const props = defineProps({
modelValue: {
type: Array<string>,
default: []
default: () => []
},
group: {
type: Object as PropType<TreeNodeData>,