feat: add tenant role templates

This commit is contained in:
Codex
2026-06-29 00:29:31 +08:00
parent 9553836ac7
commit b262e87af9
14 changed files with 504 additions and 42 deletions

View File

@@ -9,21 +9,37 @@ export interface TenantContentAuth {
userId: string;
role: string;
permissions: Record<string, unknown>;
templatePermissions: Record<string, unknown>;
}
function hasContentPermission(permissions: Record<string, unknown>) {
return permissions['*'] === true || permissions['content:*'] === true;
}
export async function requireTenantContentEditor(ctx: RequestContext): Promise<TenantContentAuth> {
const tenantId = await tenantIdFrom(ctx);
const userId = await userIdFrom(ctx);
const membership = await queryOne<{ role: string; permissions: Record<string, unknown> }>(
const membership = await queryOne<{ role: string; permissions: Record<string, unknown>; templatePermissions: Record<string, unknown> }>(
`
select role, permissions
from public.tenant_memberships
where tenant_id = $1
and user_id = $2
and status = 'active'
and role = any($3::text[])
order by case role
select tm.role, tm.permissions,
coalesce(rt.permissions, '{}'::jsonb) as "templatePermissions"
from public.tenant_memberships tm
left join public.tenant_role_templates rt
on rt.id = tm.role_template_id
and rt.tenant_id = tm.tenant_id
and rt.status = 'active'
where tm.tenant_id = $1
and tm.user_id = $2
and tm.status = 'active'
and (
tm.role = any($3::text[])
or coalesce(rt.permissions, '{}'::jsonb) ? 'content:*'
or coalesce(rt.permissions, '{}'::jsonb) ? '*'
or tm.permissions ? 'content:*'
or tm.permissions ? '*'
)
order by case tm.role
when 'tenant_owner' then 1
when 'tenant_admin' then 2
when 'tenant_operator' then 3
@@ -38,6 +54,9 @@ export async function requireTenantContentEditor(ctx: RequestContext): Promise<T
if (!membership) {
throw new HttpError(403, 'Tenant content editor access is required', 'TENANT_CONTENT_EDITOR_REQUIRED');
}
if (!CONTENT_ROLES.has(membership.role) && !hasContentPermission(membership.permissions || {}) && !hasContentPermission(membership.templatePermissions || {})) {
throw new HttpError(403, 'Tenant content editor access is required', 'TENANT_CONTENT_EDITOR_REQUIRED');
}
return { tenantId, userId, role: membership.role, permissions: membership.permissions || {} };
return { tenantId, userId, role: membership.role, permissions: membership.permissions || {}, templatePermissions: membership.templatePermissions || {} };
}