agent-permission.spec.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import {
  2. AgentPermission,
  3. buildConfigJson,
  4. buildGrantCommands,
  5. canCommand,
  6. defaultPermission,
  7. Delegation,
  8. validatePermission,
  9. } from './agent-permission';
  10. /**
  11. * 容器权限配置 —— 纯函数层。
  12. *
  13. * 界面「配了什么」必须和「生成给容器的是什么」一致,
  14. * 所以生成与校验放在这里测,不依赖组件。
  15. */
  16. const OWNER = '@owner:fmode.cn';
  17. const GROUP = '!group1:fmode.cn';
  18. const d = (over: Partial<Delegation> = {}): Delegation => ({
  19. grantee: '@peer:fmode.cn',
  20. granteeName: '同事容器',
  21. groupId: GROUP,
  22. scope: ['read', 'execute'],
  23. expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
  24. ...over,
  25. });
  26. const perm = (over: Partial<AgentPermission> = {}): AgentPermission => ({
  27. ...defaultPermission('agent-a'),
  28. ownerMxId: OWNER,
  29. delegations: [],
  30. ...over,
  31. });
  32. describe('默认配置', () => {
  33. it('默认不开放任何授权(安全基线)', () => {
  34. const p = defaultPermission('a');
  35. expect(p.delegations).toEqual([]);
  36. expect(p.allowAnyoneInGroups).toBeFalse();
  37. });
  38. });
  39. describe('buildConfigJson', () => {
  40. it('按《组长授权机制》§3.2 的结构输出', () => {
  41. const json = buildConfigJson(perm({ delegations: [d()] }));
  42. const parsed = JSON.parse(json);
  43. expect(parsed.workgroupDelegations.length).toBe(1);
  44. const item = parsed.workgroupDelegations[0];
  45. expect(item.delegateFrom).toBe('@peer:fmode.cn');
  46. expect(item.groupId).toBe(GROUP);
  47. expect(item.scope).toEqual(['read', 'execute']);
  48. // grantedBy 必须是主人,不能是别人
  49. expect(item.grantedBy).toBe(OWNER);
  50. });
  51. it('没有授权时输出空数组', () => {
  52. expect(JSON.parse(buildConfigJson(perm())).workgroupDelegations).toEqual([]);
  53. });
  54. it('缺省字段补空串而非 undefined(容器解析不炸)', () => {
  55. const json = buildConfigJson(perm({ delegations: [d({ groupId: undefined, expiresAt: undefined })] }));
  56. const item = JSON.parse(json).workgroupDelegations[0];
  57. expect(item.groupId).toBe('');
  58. expect(item.expiresAt).toBe('');
  59. });
  60. });
  61. describe('buildGrantCommands', () => {
  62. it('生成可复制的 fmode-delegate 命令', () => {
  63. const [cmd] = buildGrantCommands(perm({ delegations: [d()] }));
  64. expect(cmd).toContain('fmode-delegate grant');
  65. expect(cmd).toContain('@peer:fmode.cn');
  66. expect(cmd).toContain(`--group "${GROUP}"`);
  67. expect(cmd).toContain('--scope read,execute');
  68. expect(cmd).toContain('--days 30');
  69. });
  70. it('没有授权时不生成命令', () => {
  71. expect(buildGrantCommands(perm())).toEqual([]);
  72. });
  73. it('天数至少为 1(过期时间算出来是 0 或负数时)', () => {
  74. const soon = d({ expiresAt: new Date(Date.now() + 1000).toISOString() });
  75. const [cmd] = buildGrantCommands(perm({ delegations: [soon] }));
  76. expect(cmd).toContain('--days 1');
  77. });
  78. });
  79. describe('validatePermission', () => {
  80. it('规范配置无问题', () => {
  81. expect(validatePermission(perm({ delegations: [d()] }))).toEqual([]);
  82. });
  83. it('开启「群内任何人」会报警', () => {
  84. const issues = validatePermission(perm({ allowAnyoneInGroups: true }));
  85. expect(issues.some((i) => i.includes('任何进群的成员'))).toBeTrue();
  86. });
  87. it('管理员权限被标记为强烈不建议', () => {
  88. const issues = validatePermission(perm({ delegations: [d({ scope: ['read', 'admin'] })] }));
  89. expect(issues.some((i) => i.includes('强烈不建议'))).toBeTrue();
  90. });
  91. it('未指定群会报警', () => {
  92. const issues = validatePermission(perm({ delegations: [d({ groupId: undefined })] }));
  93. expect(issues.some((i) => i.includes('未指定群'))).toBeTrue();
  94. });
  95. it('没有权限范围会报警', () => {
  96. const issues = validatePermission(perm({ delegations: [d({ scope: [] })] }));
  97. expect(issues.some((i) => i.includes('没有选择任何权限'))).toBeTrue();
  98. });
  99. it('未设过期时间会报警', () => {
  100. const issues = validatePermission(perm({ delegations: [d({ expiresAt: undefined })] }));
  101. expect(issues.some((i) => i.includes('永久有效'))).toBeTrue();
  102. });
  103. it('过期时间已过会报警', () => {
  104. const past = d({ expiresAt: new Date(Date.now() - 86400000).toISOString() });
  105. expect(validatePermission(perm({ delegations: [past] })).some((i) => i.includes('已过'))).toBeTrue();
  106. });
  107. it('重复授权会报警', () => {
  108. const issues = validatePermission(perm({ delegations: [d(), d()] }));
  109. expect(issues.some((i) => i.includes('重复授权'))).toBeTrue();
  110. });
  111. it('缺主人身份时提示授权无人可用', () => {
  112. const issues = validatePermission(perm({ ownerMxId: undefined, delegations: [d()] }));
  113. expect(issues.some((i) => i.includes('缺少容器主人'))).toBeTrue();
  114. });
  115. });
  116. describe('canCommand(与容器逻辑对齐的预演)', () => {
  117. it('主人永远可以', () => {
  118. expect(canCommand(perm(), OWNER, GROUP).allowed).toBeTrue();
  119. expect(canCommand(perm(), OWNER, GROUP).reason).toBe('容器主人');
  120. });
  121. it('已授权的人在指定群可以', () => {
  122. const r = canCommand(perm({ delegations: [d()] }), '@peer:fmode.cn', GROUP);
  123. expect(r.allowed).toBeTrue();
  124. expect(r.reason).toContain('已授权');
  125. });
  126. // 群限制是授权的核心保护之一
  127. it('🔴 出了指定群就不行', () => {
  128. const r = canCommand(perm({ delegations: [d()] }), '@peer:fmode.cn', '!other:fmode.cn');
  129. expect(r.allowed).toBeFalse();
  130. });
  131. it('过期后不行', () => {
  132. const expired = d({ expiresAt: new Date(Date.now() - 1000).toISOString() });
  133. const r = canCommand(perm({ delegations: [expired] }), '@peer:fmode.cn', GROUP);
  134. expect(r.allowed).toBeFalse();
  135. expect(r.reason).toBe('授权已过期');
  136. });
  137. it('未授权的人不行', () => {
  138. expect(canCommand(perm(), '@stranger:fmode.cn', GROUP).allowed).toBeFalse();
  139. });
  140. it('开启「群内任何人」后任何人都可以在群里指挥', () => {
  141. const p = perm({ allowAnyoneInGroups: true });
  142. expect(canCommand(p, '@stranger:fmode.cn', GROUP).allowed).toBeTrue();
  143. // 但不在群里(无 groupId)时依然不行
  144. expect(canCommand(p, '@stranger:fmode.cn', undefined).allowed).toBeFalse();
  145. });
  146. it('scope 为空的授权不生效', () => {
  147. const r = canCommand(perm({ delegations: [d({ scope: [] })] }), '@peer:fmode.cn', GROUP);
  148. expect(r.allowed).toBeFalse();
  149. });
  150. });