feat: refining the logic of user roles (#749)

#### What type of PR is this?

/kind improvement
/milestone 2.0.1

#### What this PR does / why we need it:

完善用户角色的相关逻辑。适配 https://github.com/halo-dev/halo/pull/2865

1. 支持标识是否是系统保留角色。
2. 根据是否是系统保留角色,禁用修改和删除的操作。
3. 支持判断是否是超级管理员,如果是,默认勾选所有权限。
4. 优化 `包含 N 个权限` 文案的逻辑,超级管理员为 `包含所有权限`。
5. 优化 `基于此角色创建` 的逻辑,判断是否为超级管理员,如果是,需要设置所有角色模板到创建表单。

#### Which issue(s) this PR fixes:

Fixes https://github.com/halo-dev/halo/issues/2844

#### Screenshots:

<img width="1663" alt="image" src="https://user-images.githubusercontent.com/21301288/205965292-b8f8e556-e06b-422b-b0be-8d87a68f18be.png">
<img width="1661" alt="image" src="https://user-images.githubusercontent.com/21301288/205965333-1491c023-6726-4cdd-b970-d868a30f3296.png">

#### Special notes for your reviewer:

测试方式:

1. Halo 需要切换到 https://github.com/halo-dev/halo/pull/2865 分支。
2. 测试角色相关的所有功能。

#### Does this PR introduce a user-facing change?


```release-note
完善 Console 端用户角色的相关逻辑
```
pull/758/head
Ryan Wang 2022-12-07 12:40:53 +08:00 committed by GitHub
parent 581f7156f5
commit c70b069753
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 28 deletions

View File

@ -0,0 +1 @@
export const SUPER_ROLE_NAME = "super-role";

View File

@ -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

View File

@ -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(() => {
<p
class="mt-1 flex max-w-2xl items-center gap-2 text-sm text-gray-500"
>
<span
>包含
{{
JSON.parse(
formState.metadata.annotations?.[
rbacAnnotations.DEPENDENCIES
] || "[]"
).length
}}
个权限</span
>
<span>{{ getRoleCountText }}</span>
</p>
</div>
<div class="border-t border-gray-200">
@ -137,10 +151,11 @@ onMounted(() => {
>
<dt class="text-sm font-medium text-gray-900">类型</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<VTag>系统保留</VTag>
<VTag> {{ isSystemReserved ? "系统保留" : "自定义" }} </VTag>
</dd>
</div>
<div
v-if="false"
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">描述</dt>
@ -217,6 +232,14 @@ onMounted(() => {
</div>
<div v-if="tabActiveId === 'permissions'">
<div v-if="isSystemReserved" class="px-4 py-5">
<VAlert
title="提示"
description="系统保留的角色不支持修改,推荐基于此角色创建一个新的角色。"
class="w-full sm:w-1/4"
/>
</div>
<div>
<dl class="divide-y divide-gray-100">
<div
@ -259,12 +282,21 @@ onMounted(() => {
class="inline-flex w-72 cursor-pointer flex-row items-center gap-4 rounded-base border p-5 hover:border-primary"
>
<input
v-if="!isSuperRole"
v-model="selectedRoleTemplates"
:value="role.metadata.name"
class="h-4 w-4 rounded border-gray-300 text-indigo-600"
type="checkbox"
:disabled="isSystemReserved"
@change="handleRoleTemplateSelect"
/>
<input
v-else
class="h-4 w-4 rounded border-gray-300 text-indigo-600"
type="checkbox"
checked
disabled
/>
<div class="flex flex-1 flex-col gap-y-3">
<span class="font-medium text-gray-900">
{{
@ -301,8 +333,10 @@ onMounted(() => {
<VButton
:loading="saving"
type="secondary"
:disabled="isSystemReserved"
@click="handleUpdateRole"
>保存
>
保存
</VButton>
</div>
</div>

View File

@ -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>(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) => {
<VEntityField v-if="false" description="0 个用户" />
<VEntityField>
<template #description>
<VTag> 系统保留</VTag>
<VTag>
{{ isSystemReserved(role) ? "系统保留" : "自定义" }}
</VTag>
</template>
</VEntityField>
<VEntityField>
@ -285,6 +320,7 @@ const handleDelete = async (role: Role) => {
#dropdownItems
>
<VButton
v-if="!isSystemReserved(role)"
v-close-popper
block
type="secondary"
@ -293,6 +329,7 @@ const handleDelete = async (role: Role) => {
编辑
</VButton>
<VButton
v-if="!isSystemReserved(role)"
v-close-popper
block
type="danger"

View File

@ -97,14 +97,18 @@ export function useRoleForm(): useRoleFormReturn {
try {
saving.value = true;
if (isUpdateMode.value) {
await apiClient.extension.role.updatev1alpha1Role({
const { data } = await apiClient.extension.role.updatev1alpha1Role({
name: formState.value.metadata.name,
role: formState.value,
});
formState.value = data;
} else {
await apiClient.extension.role.createv1alpha1Role({
const { data } = await apiClient.extension.role.createv1alpha1Role({
role: formState.value,
});
formState.value = data;
}
} catch (e) {
console.error(e);