原子指令授权
原子指令授权
主要解决这类问题:谁
能对哪个范围内
的原子数据
执行什么操作
- 授权策略
CabloyJS采用白名单策略
,所有角色的授权都必须显式设置(superuser
也不例外)
- 授权内容
针对原子类型
的原子指令
授权,如以下授权记录:
角色 | 原子类型 | 原子指令 |
---|---|---|
system | party | create |
- 资源范围
在授权时可以指定权限的资源范围
,在CabloyJS中资源范围也是角色
,如以下授权记录
角色 | 原子类型 | 原子指令 | 资源范围 |
---|---|---|---|
system | party | read | 财务部 |
角色
system
仅能读取财务部
创建的party
数据
财务部
也是角色
授权途径
授权途径有三种:人工授权
、初始授权
、测试授权
- 人工授权
如果某些权限只有在实际部署或运行时才能决定,可通过管理界面
进行授权操作
- 初始授权
如果需要在系统运行时,预先初始化权限,可以在Bean组件version.manager
的方法init
中通过代码分配权限。比如,给模版角色system
分配权限
初始授权
在测试环境
、开发环境
和生产环境
均有效
src/module/test-party/backend/src/bean/version.manager.js
async init(options) {
// only in test/local
if (!app.meta.isTest && !app.meta.isLocal) return;
// init
if (options.version === 1) {
// types
for (const name of [ 'Birthday', 'Dance', 'Garden' ]) {
await this.ctx.model.partyType.insert({ name });
}
// add role rights
const roleRights = [
// basic
{ roleName: 'system', action: 'create' },
{ roleName: 'system', action: 'read', scopeNames: 'authenticated' },
{ roleName: 'system', action: 'write', scopeNames: 0 },
{ roleName: 'system', action: 'delete', scopeNames: 0 },
{ roleName: 'system', action: 'clone', scopeNames: 0 },
{ roleName: 'system', action: 'deleteBulk' },
{ roleName: 'system', action: 'exportBulk' },
];
await this.ctx.bean.role.addRoleRightBatch({ atomClassName: 'party', roleRights });
}
}
- 测试授权
为了测试与开发的便利,还可以在开发阶段
预先初始化权限,可以在Bean组件version.manager
的方法test
中通过代码分配权限。比如,给某些测试角色分配权限
测试授权
仅在测试环境
和开发环境
有效
src/module/test-party/backend/src/bean/version.manager.js
const VersionTestFn = require('./version/test.js');
async test() {
const versionTest = new (VersionTestFn(this.ctx))();
await versionTest.run();
}
授权API
- addRoleRight
a-base/backend/src/config/middleware/adapter/role.js
async addRoleRight({ roleId, atomClassId, action, scope })
名称 | 说明 |
---|---|
roleId | 需要被授权的角色Id |
atomClassId | 原子类型Id |
action | 需要授权的指令code |
scope | 资源范围 |
scope支持以下值:
0
: 操作自己创建的原子数据roleId
: 操作roleId
及以下所有子角色的用户创建的原子数据roleIds
: roleId数组
- addRoleRightBatch
a-base/backend/src/config/middleware/adapter/role.js
async addRoleRightBatch({ module, atomClassName, atomClassIdParent = 0, roleRights })
名称 | 说明 |
---|---|
module | 模块名称,如果为空就使用当前模块名称 |
atomClassName | 原子类型名称 |
roleRights | 授权记录数组 |
授权判断方式
可以通过中间件
或Api
进行授权的判断
- 中间件判断
CabloyJS使用中间件right
封装了授权判断的逻辑,只需在后端路由上配置相应的中间件参数即可
src/module/test-party/backend/src/routes.js
{ method: 'post', path: 'test/atom/checkRightCreate', controller: 'testAtomRight', middlewares: 'test',
meta: { right: { type: 'atom', action: 1 } },
},
{ method: 'post', path: 'test/atom/checkRightRead', controller: 'testAtomRight', middlewares: 'test',
meta: { right: { type: 'atom', action: 2 } },
},
{ method: 'post', path: 'test/atom/checkRightWrite', controller: 'testAtomRight', middlewares: 'test',
meta: { right: { type: 'atom', action: 3 } },
},
名称 | 说明 |
---|---|
right | 全局中间件right ,默认处于开启状态,只需配置参数即可 |
type | 授权类型,这里是原子 授权 |
action | 原子指令代码 |
- Api判断
src/module/test-party/backend/src/controller/test/atom/all.js
// atomClass
const atomClass = await this.ctx.meta.atomClass.get({ atomClassName: 'party' });
// userIds
const userIds = this.ctx.cache.mem.get('userIds');
1. 指令create
const checkRightCreates = [[ 'Tom', true ], [ 'Jimmy', true ], [ 'Smith', false ]];
for (const [ userName, right ] of checkRightCreates) {
const res = await this.ctx.meta.atom.checkRightCreate({
atomClass,
user: { id: userIds[userName] },
});
assert.equal(!!res, right, userName);
}
2. 指令read
const checkRightReads = [[ 'Tom', partyKey.atomId, true ]];
for (const [ userName, atomId, right ] of checkRightReads) {
const res = await this.ctx.meta.atom.checkRightRead({
atom: { id: atomId },
user: { id: userIds[userName] },
});
assert.equal(!!res, right, userName);
}
3. 指令write
const checkRightWrites = [[ 'Tom', partyKeyArchive.atomId, true ], [ 'Tomson', partyKeyArchive.atomId, false ]];
for (const [ userName, atomId, right ] of checkRightWrites) {
const res = await this.ctx.bean.atom.checkRightAction({
atom: { id: atomId },
action: this.ctx.constant.module('a-base').atom.action.write,
user: { id: userIds[userName] },
});
assert.equal(!!res, right, userName);
}
4. 指令delete
const checkRightDeletes = [[ 'Tom', partyKeyArchive.atomId, true ], [ 'Tomson', partyKeyArchive.atomId, false ]];
for (const [ userName, atomId, right ] of checkRightDeletes) {
const res = await this.ctx.bean.atom.checkRightAction({
atom: { id: atomId },
action: this.ctx.constant.module('a-base').atom.action.delete,
user: { id: userIds[userName] },
});
assert.equal(!!res, right, userName);
}
评论: