feat(auth): assign default editor role to verified users without roles
Update bootstrap rules to grant 'editor' role to verified users Backfill existing verified users without roles in schema.sql Apply default role automatically during email verification
This commit is contained in:
@@ -159,7 +159,8 @@
|
|||||||
- Bootstrap 规则:
|
- Bootstrap 规则:
|
||||||
- 启动时若已有已验证用户但没有任何 `owner` 用户,系统自动将最早完成验证的用户加入 `owner` 角色。
|
- 启动时若已有已验证用户但没有任何 `owner` 用户,系统自动将最早完成验证的用户加入 `owner` 角色。
|
||||||
- 若系统还没有 `owner` 用户,首个完成邮箱验证的用户自动加入 `owner` 角色。
|
- 若系统还没有 `owner` 用户,首个完成邮箱验证的用户自动加入 `owner` 角色。
|
||||||
- 系统初始化只补齐默认角色、默认权限和 Owner 关联;不覆盖管理员对默认角色/权限元数据或角色权限分配的配置。
|
- 已完成邮箱验证且没有任何角色的用户默认加入 `editor` 角色;已有角色关系的用户不被覆盖。
|
||||||
|
- 系统初始化只补齐默认角色、默认权限、Owner 关联和无角色已验证用户的默认 Editor 关联;不覆盖管理员对默认角色/权限元数据或角色权限分配的配置。
|
||||||
- 新建权限会自动关联到 `owner` 角色,确保 Owner 始终拥有可用权限全集;`owner` 角色的权限分配不能在管理端被手动删改。
|
- 新建权限会自动关联到 `owner` 角色,确保 Owner 始终拥有可用权限全集;`owner` 角色的权限分配不能在管理端被手动删改。
|
||||||
- 系统必须始终至少保留一个拥有 `admin.permissions.update` 且可管理权限的有效用户;核心 RBAC 管理权限(`admin.access`、`admin.users.*`、`admin.roles.*`、`admin.permissions.*`)不能被禁用或删除;不能删除最后一个 Owner,不能移除最后一个 Owner 的关键权限能力。
|
- 系统必须始终至少保留一个拥有 `admin.permissions.update` 且可管理权限的有效用户;核心 RBAC 管理权限(`admin.access`、`admin.users.*`、`admin.roles.*`、`admin.permissions.*`)不能被禁用或删除;不能删除最后一个 Owner,不能移除最后一个 Owner 的关键权限能力。
|
||||||
- 权限管理能力本身也通过权限控制;只有拥有相应管理权限的用户可以查看、新增、编辑、删除权限、角色和用户角色关系。
|
- 权限管理能力本身也通过权限控制;只有拥有相应管理权限的用户可以查看、新增、编辑、删除权限、角色和用户角色关系。
|
||||||
|
|||||||
@@ -368,6 +368,19 @@ CROSS JOIN roles r
|
|||||||
WHERE r.key = 'owner'
|
WHERE r.key = 'owner'
|
||||||
ON CONFLICT DO NOTHING;
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
INSERT INTO user_roles (user_id, role_id)
|
||||||
|
SELECT u.id, r.id
|
||||||
|
FROM users u
|
||||||
|
CROSS JOIN roles r
|
||||||
|
WHERE u.email_verified_at IS NOT NULL
|
||||||
|
AND r.key = 'editor'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM user_roles ur
|
||||||
|
WHERE ur.user_id = u.id
|
||||||
|
)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS system_wording_keys (
|
CREATE TABLE IF NOT EXISTS system_wording_keys (
|
||||||
key text PRIMARY KEY,
|
key text PRIMARY KEY,
|
||||||
module text NOT NULL,
|
module text NOT NULL,
|
||||||
|
|||||||
@@ -422,6 +422,24 @@ async function ensureOwnerRoleForUser(client: DbClient, userId: number): Promise
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function ensureDefaultEditorRoleForUser(client: DbClient, userId: number): Promise<void> {
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
INSERT INTO user_roles (user_id, role_id)
|
||||||
|
SELECT $1, r.id
|
||||||
|
FROM roles r
|
||||||
|
WHERE r.key = 'editor'
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM user_roles ur
|
||||||
|
WHERE ur.user_id = $1
|
||||||
|
)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
`,
|
||||||
|
[userId]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function toRoleSummary(row: RoleRow): RoleSummary {
|
function toRoleSummary(row: RoleRow): RoleSummary {
|
||||||
return {
|
return {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
@@ -832,6 +850,7 @@ export async function verifyEmail(payload: Record<string, unknown>, locale = def
|
|||||||
user.id
|
user.id
|
||||||
]);
|
]);
|
||||||
await ensureOwnerRoleForUser(client, user.id);
|
await ensureOwnerRoleForUser(client, user.id);
|
||||||
|
await ensureDefaultEditorRoleForUser(client, user.id);
|
||||||
|
|
||||||
const publicUser = await publicUserById(user.id, client);
|
const publicUser = await publicUserById(user.id, client);
|
||||||
return { message: await authMessage(locale, 'emailVerified'), user: publicUser ?? toPublicUser(user) };
|
return { message: await authMessage(locale, 'emailVerified'), user: publicUser ?? toPublicUser(user) };
|
||||||
|
|||||||
Reference in New Issue
Block a user