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(() => {
+
+
+
+
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) => {
- 系统保留
+
+ {{ isSystemReserved(role) ? "系统保留" : "自定义" }}
+
@@ -285,6 +320,7 @@ const handleDelete = async (role: Role) => {
#dropdownItems
>
{
编辑