はじめに
マルチテナント プラットフォームではデータの分離を確保する必要があります。テナント A はテナント B のデータにアクセスできません。 RBAC (Role-Based Access Control) は、誰が何を実行できるかを制御します。この記事では、完全なマルチテナント RBAC システムを実装します。
1. テナントの分離
1.1 データ分割戦略
// Approach: Shared database, tenant_id column
// Mỗi query phải filter theo tenantId
// packages/db/src/middleware/tenant-middleware.ts
export function tenantScope(tenantId: string) {
return {
// PostgreSQL: Drizzle ORM filter
pg: <T extends { tenantId: string }>(query: any) =>
query.where(eq(schema.tenantId, tenantId)),
// MongoDB: add tenantId to all queries
mongo: (filter: Record<string, unknown>) => ({
...filter,
tenantId,
}),
};
}
// Usage in service layer
async function getSessions(tenantId: string, userId: string) {
return db.collection('sessions')
.find(tenantScope(tenantId).mongo({ userId }))
.sort({ updatedAt: -1 })
.toArray();
}
1.2 テナントの設定
interface TenantSettings {
// LLM Configuration
llm: {
defaultProvider: string;
defaultModel: string;
apiKeys: Record<string, string>; // encrypted
maxTokensPerRequest: number;
};
// Usage Quotas
quotas: {
maxUsersPerTenant: number;
maxRequestsPerDay: number;
maxStorageMB: number;
maxCollections: number;
};
// Features
features: {
ragEnabled: boolean;
workflowsEnabled: boolean;
mcpEnabled: boolean;
customDomainsEnabled: boolean;
};
// Branding
branding: {
logo?: string;
primaryColor?: string;
appName?: string;
};
}
2. RBAC システム
2.1 権限の定義
// packages/core/src/auth/permissions.ts
export const PERMISSIONS = {
// Chat
'chat:send': 'Send chat messages',
'chat:view_history': 'View chat history',
'chat:delete': 'Delete chat sessions',
'chat:export': 'Export chat data',
// Models
'models:view': 'View available models',
'models:configure': 'Configure model settings',
// Knowledge Base (RAG)
'knowledge:view': 'View knowledge collections',
'knowledge:create': 'Create collections',
'knowledge:upload': 'Upload documents',
'knowledge:delete': 'Delete collections/documents',
// Workflows
'workflows:view': 'View workflows',
'workflows:create': 'Create workflows',
'workflows:execute': 'Execute workflows',
'workflows:delete': 'Delete workflows',
// Skills & Domains
'skills:view': 'View available skills',
'skills:activate': 'Activate/deactivate skills',
'skills:manage': 'Manage skill configurations',
// Users & Roles
'users:view': 'View user list',
'users:invite': 'Invite new users',
'users:remove': 'Remove users',
'roles:view': 'View roles',
'roles:manage': 'Create/edit/delete roles',
// Tenant
'tenant:settings': 'Manage tenant settings',
'tenant:billing': 'View/manage billing',
'tenant:api_keys': 'Manage API keys',
// Monitoring
'monitoring:view': 'View usage analytics',
'monitoring:logs': 'View system logs',
'monitoring:audit': 'View audit trail',
} as const;
export type Permission = keyof typeof PERMISSIONS;
2.2 システムの役割
export const SYSTEM_ROLES = {
owner: {
name: 'Owner',
permissions: Object.keys(PERMISSIONS) as Permission[], // All 60 permissions
description: 'Full access to everything',
},
admin: {
name: 'Admin',
permissions: Object.keys(PERMISSIONS).filter(
p => !p.startsWith('tenant:') // Everything except tenant management
) as Permission[],
description: 'Manage users, content, and settings',
},
member: {
name: 'Member',
permissions: [
'chat:send', 'chat:view_history',
'models:view',
'knowledge:view', 'knowledge:upload',
'workflows:view', 'workflows:execute',
'skills:view', 'skills:activate',
'monitoring:view',
] as Permission[],
description: 'Standard user access',
},
viewer: {
name: 'Viewer',
permissions: [
'chat:send', 'chat:view_history',
'models:view',
'knowledge:view',
'workflows:view',
'skills:view',
] as Permission[],
description: 'Read-only access',
},
};
2.3 権限チェッカー
// packages/core/src/auth/rbac.ts
export class RBACService {
private db: DrizzleDB;
async hasPermission(
userId: string,
tenantId: string,
permission: Permission,
): Promise<boolean> {
const userRoles = await this.db
.select()
.from(schema.userRoles)
.innerJoin(schema.roles, eq(schema.roles.id, schema.userRoles.roleId))
.where(
and(
eq(schema.userRoles.userId, userId),
eq(schema.roles.tenantId, tenantId),
),
);
for (const ur of userRoles) {
const perms = ur.roles.permissions as string[];
if (perms.includes(permission)) return true;
}
return false;
}
async getUserPermissions(
userId: string,
tenantId: string,
): Promise<Permission[]> {
const userRoles = await this.db
.select()
.from(schema.userRoles)
.innerJoin(schema.roles, eq(schema.roles.id, schema.userRoles.roleId))
.where(
and(
eq(schema.userRoles.userId, userId),
eq(schema.roles.tenantId, tenantId),
),
);
const permissions = new Set<Permission>();
for (const ur of userRoles) {
const perms = ur.roles.permissions as Permission[];
perms.forEach(p => permissions.add(p));
}
return Array.from(permissions);
}
}
3. 監査証跡
// Mọi action quan trọng đều được log
async function auditLog(
tenantId: string,
userId: string,
action: string,
details: Record<string, unknown>,
) {
await db.collection('audit_logs').insertOne({
tenantId,
userId,
action,
details,
ip: context.ip,
userAgent: context.userAgent,
createdAt: new Date(),
});
}
// Usage
await auditLog(tenantId, userId, 'role:assigned', {
targetUserId: newUser.id,
roleName: 'member',
});
4. まとめ
- テナント分離 — 共有 DB
tenantIdフィルタリング - 60 権限 — 機能ごとにグループ化された詳細な権限
- 4 つのシステム ロール — 所有者、管理者、メンバー、閲覧者
- カスタム ロール — テナントは独自のロールを作成できます
- 監査証跡 — すべての重要なアクションが記録されます
次の記事: チャット チャネル — マルチプラットフォームのメッセージング接続。