feat: 프로필별 플레이 감사 조회와 권한 검사 연결
This commit is contained in:
@@ -10,6 +10,20 @@ export interface AdminCapabilityDefinition {
|
||||
}
|
||||
|
||||
export const ADMIN_CAPABILITIES: readonly AdminCapabilityDefinition[] = [
|
||||
{
|
||||
permission: 'admin.playAudit.read',
|
||||
label: '플레이 감사 조회',
|
||||
description: '지정 profile의 장수·도시·재정과 플레이 이력을 조회합니다.',
|
||||
risk: 'HIGH',
|
||||
scope: 'PROFILE',
|
||||
},
|
||||
{
|
||||
permission: 'admin.playAudit.accounts',
|
||||
label: '플레이 감사 계정 조사',
|
||||
description: '플레이 감사 권한을 가진 profile에서 계정 시도·접속지 연관 자료를 조사합니다.',
|
||||
risk: 'HIGH',
|
||||
scope: 'GLOBAL',
|
||||
},
|
||||
{
|
||||
permission: 'admin.notice.manage',
|
||||
label: 'Gateway 공지 관리',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import { decryptGameSessionToken } from '@sammo-ts/common/auth/gameToken';
|
||||
import fastify, { type FastifyRequest } from 'fastify';
|
||||
import { fastifyTRPCPlugin } from '@trpc/server/adapters/fastify';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
@@ -248,6 +250,46 @@ describe('admin security over HTTP transport', () => {
|
||||
expect(harness.flushes).toEqual([]);
|
||||
});
|
||||
|
||||
it('grants scoped play audit roles, flushes old tokens and transports the exact scope', async () => {
|
||||
const grant = 'admin.playAudit.read:che:default';
|
||||
const harness = await createHarness(['admin.users.manage', grant, 'admin.playAudit.accounts']);
|
||||
const granted = await postTrpc(
|
||||
harness.baseUrl,
|
||||
'admin.users.updateRoles',
|
||||
{
|
||||
userId: harness.target.id,
|
||||
roles: [grant, 'admin.playAudit.accounts'],
|
||||
mode: 'grant',
|
||||
reason: '감사 조회 권한 부여',
|
||||
},
|
||||
harness.adminSessionToken
|
||||
);
|
||||
expect(granted.response.status).toBe(200);
|
||||
expect(harness.flushes).toEqual([{ userId: harness.target.id, reason: 'admin-roles-updated' }]);
|
||||
const issued = await postTrpc(harness.baseUrl, 'auth.issueGameSession', {
|
||||
sessionToken: harness.targetSessionToken,
|
||||
profile: 'che:default',
|
||||
});
|
||||
expect(issued.response.status).toBe(200);
|
||||
const body = z.object({ result: z.object({ data: z.object({ gameToken: z.string() }) }) }).parse(issued.body);
|
||||
const payload = decryptGameSessionToken(body.result.data.gameToken, 'transport-e2e-secret');
|
||||
expect(payload?.profile).toBe('che:default');
|
||||
expect(payload?.user.roles).toEqual(['user', grant, 'admin.playAudit.accounts']);
|
||||
const rejected = await postTrpc(
|
||||
harness.baseUrl,
|
||||
'admin.users.updateRoles',
|
||||
{
|
||||
userId: harness.target.id,
|
||||
roles: ['admin.playAudit.read:*'],
|
||||
mode: 'grant',
|
||||
reason: '범위 확대 거부 검증',
|
||||
},
|
||||
harness.adminSessionToken
|
||||
);
|
||||
expect(rejected.response.status).toBe(403);
|
||||
expect(harness.flushes).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('accepts an equal scoped role and rejects wildcard escalation without mutating roles', async () => {
|
||||
const harness = await createHarness();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user