原子指令授权

原子指令授权主要解决这类问题:能对哪个范围内原子数据执行什么操作

- 授权策略

CabloyJS采用白名单策略,所有角色的授权都必须显式设置(superuser也不例外)

- 授权内容

针对原子类型原子指令授权,如以下授权记录:

角色 原子类型 原子指令
system party create

- 数据范围

在授权时可以指定权限的数据范围,在CabloyJS中数据范围也是角色,如以下授权记录

角色 原子类型 原子指令 数据范围
system party read 财务部

角色system仅能读取财务部创建的party数据

财务部也是角色

授权途径

授权途径有三种:人工授权初始授权测试授权

- 人工授权

如果某些权限只有在实际部署或运行时才能决定,可通过管理界面进行授权操作

- 初始授权

如果需要在系统运行时,预先初始化权限,可以在Bean组件version.manager的方法init中通过代码分配权限。比如,给模版角色system分配权限

初始授权测试环境开发环境生产环境均有效

src/suite-vendor/test-party/modules/test-party/backend/src/bean/version.manager.js

  1. 1async init(options) {
  2. 2 // only in test/local
  3. 3 if (!app.meta.isTest && !app.meta.isLocal) return;
  4. 4
  5. 5 // init
  6. 6 if (options.version === 1) {
  7. 7 // types
  8. 8 for (const name of [ 'Birthday', 'Dance', 'Garden' ]) {
  9. 9 await this.ctx.model.partyType.insert({ name });
  10. 10 }
  11. 11 // add role rights
  12. 12 const roleRights = [
  13. 13 // basic
  14. 14 { roleName: 'system', action: 'create' },
  15. 15 { roleName: 'system', action: 'read', scopeNames: 'authenticated' },
  16. 16 { roleName: 'system', action: 'write', scopeNames: 0 },
  17. 17 { roleName: 'system', action: 'delete', scopeNames: 0 },
  18. 18 { roleName: 'system', action: 'clone', scopeNames: 0 },
  19. 19 { roleName: 'system', action: 'deleteBulk' },
  20. 20 { roleName: 'system', action: 'exportBulk' },
  21. 21 ];
  22. 22 await this.ctx.bean.role.addRoleRightBatch({ atomClassName: 'party', roleRights });
  23. 23 }
  24. 24
  25. 25}

- 测试授权

为了测试与开发的便利,还可以在开发阶段预先初始化权限,可以在Bean组件version.manager的方法test中通过代码分配权限。比如,给某些测试角色分配权限

测试授权仅在测试环境开发环境有效

src/suite-vendor/test-party/modules/test-party/backend/src/bean/version.manager.js

  1. 1const VersionTestFn = require('./version/test.js');
  2. 2
  3. 3async test() {
  4. 4 const versionTest = new (VersionTestFn(this.ctx))();
  5. 5 await versionTest.run();
  6. 6}

授权API

- addRoleRight

src/module-system/a-base-sync/backend/src/bean/bean.role/bean.role_atomRights.js

  1. 1async addRoleRight({ roleId, atomClassId, action, scope })
名称 说明
roleId 需要被授权的角色Id
atomClassId 原子类型Id
action 需要授权的指令code
scope 数据范围

scope支持以下值:

  • 0: 操作自己创建的原子数据
  • roleId: 操作roleId及以下所有子角色的用户创建的原子数据
  • roleIds: roleId数组

- addRoleRightBatch

src/module-system/a-base-sync/backend/src/bean/bean.role/bean.role_atomRights.js

  1. 1async addRoleRightBatch({ module, atomClassName, roleRights })
名称 说明
module 模块名称,如果为空就使用当前模块名称
atomClassName 原子类型名称
roleRights 授权记录数组

授权判断方式

可以通过中间件Api进行授权的判断

- 中间件判断

CabloyJS使用中间件right封装了授权判断的逻辑,只需在后端路由上配置相应的中间件参数即可

src/suite-vendor/test-party/modules/test-party/backend/src/routes.js

  1. 1 {
  2. 2 method: 'post',
  3. 3 path: 'test/atom/checkRightCreate',
  4. 4 controller: 'testAtomRight',
  5. 5 middlewares: 'test',
  6. 6 meta: { right: { type: 'atom', atomClass: 'test-party:party', action: 'create' } },
  7. 7 },
  8. 8 {
  9. 9 method: 'post',
  10. 10 path: 'test/atom/checkRightRead',
  11. 11 controller: 'testAtomRight',
  12. 12 middlewares: 'test',
  13. 13 meta: { right: { type: 'atom', atomClass: 'test-party:party', action: 'read' } },
  14. 14 },
  15. 15 {
  16. 16 method: 'post',
  17. 17 path: 'test/atom/checkRightWrite',
  18. 18 controller: 'testAtomRight',
  19. 19 middlewares: 'test',
  20. 20 meta: { right: { type: 'atom', atomClass: 'test-party:party', action: 'write' } },
  21. 21 },
  22. 22 {
  23. 23 method: 'post',
  24. 24 path: 'test/atom/checkRightAction',
  25. 25 controller: 'testAtomRight',
  26. 26 middlewares: 'test',
  27. 27 meta: { right: { type: 'atom', atomClass: 'test-party:party', action: 'partyOver' } },
  28. 28 },
名称 说明
right 全局中间件right,默认处于开启状态,只需配置参数即可
type 授权类型,这里是原子授权
atomClass 原子类型
action 原子指令

- Api判断

src/suite-vendor/test-party/modules/test-party/backend/src/controller/test/atom/all.js

  1. 1// atomClass
  2. 2const atomClass = await this.ctx.bean.atomClass.get({ atomClassName: 'party' });
  3. 3// userIds
  4. 4const userIds = this.ctx.cache.mem.get('userIds');

1. 指令create

  1. 1const checkRightCreates = [[ 'Tom', true ], [ 'Jimmy', true ], [ 'Smith', false ]];
  2. 2for (const [ userName, right ] of checkRightCreates) {
  3. 3 const res = await this.ctx.bean.atom.checkRightCreate({
  4. 4 atomClass,
  5. 5 user: { id: userIds[userName] },
  6. 6 });
  7. 7 assert.equal(!!res, right, userName);
  8. 8}

2. 指令read

  1. 1const checkRightReads = [[ 'Tom', partyKey.atomId, true ]];
  2. 2for (const [ userName, atomId, right ] of checkRightReads) {
  3. 3 const res = await this.ctx.bean.atom.checkRightRead({
  4. 4 atom: { id: atomId },
  5. 5 user: { id: userIds[userName] },
  6. 6 });
  7. 7 assert.equal(!!res, right, userName);
  8. 8}

3. 指令write

  1. 1const checkRightWrites = [[ 'Tom', partyKeyFormal.atomId, true ], [ 'Tomson', partyKeyFormal.atomId, false ]];
  2. 2for (const [ userName, atomId, right ] of checkRightWrites) {
  3. 3 const res = await this.ctx.bean.atom.checkRightAction({
  4. 4 atom: { id: atomId },
  5. 5 action: 'write',
  6. 6 user: { id: userIds[userName] },
  7. 7 });
  8. 8 assert.equal(!!res, right, userName);
  9. 9}

4. 指令delete

  1. 1const checkRightDeletes = [[ 'Tom', partyKeyFormal.atomId, true ], [ 'Tomson', partyKeyFormal.atomId, false ]];
  2. 2for (const [ userName, atomId, right ] of checkRightDeletes) {
  3. 3 const res = await this.ctx.bean.atom.checkRightAction({
  4. 4 atom: { id: atomId },
  5. 5 action: 'delete',
  6. 6 user: { id: userIds[userName] },
  7. 7 });
  8. 8 assert.equal(!!res, right, userName);
  9. 9}