diff --git a/src/constants/constants.ts b/src/constants/constants.ts new file mode 100644 index 00000000..1a9befd1 --- /dev/null +++ b/src/constants/constants.ts @@ -0,0 +1 @@ +export const SUPER_ROLE_NAME = "super-role"; diff --git a/src/constants/labels.ts b/src/constants/labels.ts index c5df31e2..1cb0ac07 100644 --- a/src/constants/labels.ts +++ b/src/constants/labels.ts @@ -6,6 +6,7 @@ export enum pluginLabels { // role export enum roleLabels { TEMPLATE = "halo.run/role-template", + SYSTEM_RESERVED = "rbac.authorization.halo.run/system-reserved", } // post diff --git a/src/modules/system/roles/RoleDetail.vue b/src/modules/system/roles/RoleDetail.vue index a4263a0b..76ce43e5 100644 --- a/src/modules/system/roles/RoleDetail.vue +++ b/src/modules/system/roles/RoleDetail.vue @@ -8,17 +8,19 @@ import { VTabbar, VTag, VAvatar, + VAlert, } from "@halo-dev/components"; import { useRoute } from "vue-router"; -import { onMounted, ref, watch } from "vue"; +import { computed, onMounted, ref, watch } from "vue"; import { apiClient } from "@/utils/api-client"; -import { pluginLabels } from "@/constants/labels"; +import { pluginLabels, roleLabels } from "@/constants/labels"; import { rbacAnnotations } from "@/constants/annotations"; import { useRoleForm, useRoleTemplateSelection, } from "@/modules/system/roles/composables/use-role"; import { useUserFetch } from "@/modules/system/users/composables/use-user"; +import { SUPER_ROLE_NAME } from "@/constants/constants"; const route = useRoute(); @@ -31,6 +33,28 @@ const { formState, saving, handleCreateOrUpdate } = useRoleForm(); const { users } = useUserFetch({ fetchOnMounted: false }); +const isSystemReserved = computed(() => { + return ( + formState.value.metadata.labels?.[roleLabels.SYSTEM_RESERVED] === "true" + ); +}); + +const isSuperRole = computed(() => { + return formState.value.metadata.name === SUPER_ROLE_NAME; +}); + +const getRoleCountText = computed(() => { + if (formState.value.metadata.name === SUPER_ROLE_NAME) { + return `包含所有权限`; + } + + const dependenciesCount = JSON.parse( + formState.value.metadata.annotations?.[rbacAnnotations.DEPENDENCIES] || "[]" + ).length; + + return `包含 ${dependenciesCount} 个权限`; +}); + watch( () => selectedRoleTemplates.value, (newValue) => { @@ -97,17 +121,7 @@ onMounted(() => {

- 包含 - {{ - JSON.parse( - formState.metadata.annotations?.[ - rbacAnnotations.DEPENDENCIES - ] || "[]" - ).length - }} - 个权限 + {{ getRoleCountText }}

@@ -137,10 +151,11 @@ onMounted(() => { >
类型
- 系统保留 + {{ isSystemReserved ? "系统保留" : "自定义" }}
描述
@@ -217,6 +232,14 @@ onMounted(() => {
+
+ +
+
{ class="inline-flex w-72 cursor-pointer flex-row items-center gap-4 rounded-base border p-5 hover:border-primary" > +
{{ @@ -301,8 +333,10 @@ onMounted(() => { 保存 + > + 保存
diff --git a/src/modules/system/roles/RoleList.vue b/src/modules/system/roles/RoleList.vue index f80667f6..5909091b 100644 --- a/src/modules/system/roles/RoleList.vue +++ b/src/modules/system/roles/RoleList.vue @@ -33,6 +33,8 @@ import cloneDeep from "lodash.clonedeep"; import { apiClient } from "@/utils/api-client"; import Fuse from "fuse.js"; import { usePermission } from "@/utils/permission"; +import { roleLabels } from "@/constants/labels"; +import { SUPER_ROLE_NAME } from "@/constants/constants"; const { currentUserHasPermission } = usePermission(); @@ -63,6 +65,22 @@ const searchResults = computed(() => { return fuse?.search(keyword.value).map((item) => item.item); }); +const isSystemReserved = (role: Role) => { + return role.metadata.labels?.[roleLabels.SYSTEM_RESERVED] === "true"; +}; + +const getRoleCountText = (role: Role) => { + if (role.metadata.name === SUPER_ROLE_NAME) { + return `包含所有权限`; + } + + const dependenciesCount = JSON.parse( + role.metadata.annotations?.[rbacAnnotations.DEPENDENCIES] || "[]" + ).length; + + return `包含 ${dependenciesCount} 个权限`; +}; + const handleOpenEditingModal = (role: Role) => { selectedRole.value = role; editingModal.value = true; @@ -73,10 +91,33 @@ const onEditingModalClose = () => { handleFetchRoles(); }; -const handleCloneRole = (role: Role) => { +const handleCloneRole = async (role: Role) => { const roleToCreate = cloneDeep(role); roleToCreate.metadata.name = ""; + roleToCreate.metadata.generateName = "role-"; roleToCreate.metadata.creationTimestamp = undefined; + + // 移除系统保留标识 + delete roleToCreate.metadata.labels?.[roleLabels.SYSTEM_RESERVED]; + + // 如果是超级管理员角色,那么需要获取到所有角色模板并填充到表单 + if (role.metadata.name === SUPER_ROLE_NAME) { + const { data } = await apiClient.extension.role.listv1alpha1Role({ + page: 0, + size: 0, + labelSelector: [`${roleLabels.TEMPLATE}=true`, "!halo.run/hidden"], + }); + const roleTemplateNames = data.items.map((item) => item.metadata.name); + if (roleToCreate.metadata.annotations) { + roleToCreate.metadata.annotations[rbacAnnotations.DEPENDENCIES] = + JSON.stringify(roleTemplateNames); + } else { + roleToCreate.metadata.annotations = { + [rbacAnnotations.DEPENDENCIES]: JSON.stringify(roleTemplateNames), + }; + } + } + selectedRole.value = roleToCreate; editingModal.value = true; }; @@ -242,15 +283,7 @@ const handleDelete = async (role: Role) => { role.metadata.annotations?.[rbacAnnotations.DISPLAY_NAME] || role.metadata.name " - :description="`包含 - ${ - JSON.parse( - role.metadata.annotations?.[ - rbacAnnotations.DEPENDENCIES - ] || '[]' - ).length - } - 个权限`" + :description="getRoleCountText(role)" :route="{ name: 'RoleDetail', params: { @@ -269,7 +302,9 @@ const handleDelete = async (role: Role) => { @@ -285,6 +320,7 @@ const handleDelete = async (role: Role) => { #dropdownItems > { 编辑