Atom Authorization

Atom authorization mainly solves such problems: who can perform which actions of atom data within which data scope

Authorization Record

Authorization of atom action for atomClass, such as the following authorization record:

Role AtomClass Atom Action
system party create

Data Scope of Authorization

When authorizing, you can specify the data scope of the permission, such as the following authorization record:

Role AtomClass Atom Action Data Scope
system party read finance department

The role system can only read party data of finance department

Authorization Ways

There are three ways of authorization: Artificial Authorization, Initial Authorization, Test Authorization

The API methods for initial authorization and test authorization are as follows:

addRoleRight

a-base/backend/src/config/middleware/adapter/role.js

  1. 1async addRoleRight({ roleId, atomClassId, action, scope })
Name Description
roleId RoleId to be authorized
atomClassId Atom Class Id
action Atom Action Code
scope Data Scope

scope supports the following values:

  • 0: Can operate the atom data created by self
  • roleId: Can operate the atom data created by users belonged to roleId and roleId’s child roles
  • roleIds: Array of roleId

addRoleRightBatch

a-base/backend/src/config/middleware/adapter/role.js

  1. 1// const roleRights = [
  2. 2// { roleName: 'cms-writer', action: 'create' },
  3. 3// { roleName: 'cms-writer', action: 'write', scopeNames: 0 },
  4. 4// { roleName: 'cms-writer', action: 'delete', scopeNames: 0 },
  5. 5// { roleName: 'cms-writer', action: 'read', scopeNames: 'authenticated' },
  6. 6// { roleName: 'cms-publisher', action: 'read', scopeNames: 'authenticated' },
  7. 7// { roleName: 'cms-publisher', action: 'write', scopeNames: 'authenticated' },
  8. 8// { roleName: 'cms-publisher', action: 'publish', scopeNames: 'authenticated' },
  9. 9// { roleName: 'root', action: 'read', scopeNames: 'authenticated' },
  10. 10// ];
  11. 11async addRoleRightBatch({ module, atomClassName, roleRights })
Name Description
module module name. If it is empty, the current module name will be used
atomClassName atom class name
roleRights array of authorization records

For Example

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

  1. 1// add role rights
  2. 2const roleRights = [
  3. 3 { roleName: 'system', action: 'create' },
  4. 4 { roleName: 'system', action: 'write', scopeNames: 0 },
  5. 5 { roleName: 'system', action: 'delete', scopeNames: 0 },
  6. 6 { roleName: 'system', action: 'read', scopeNames: 'authenticated' },
  7. 7 { roleName: 'system', action: 'review', scopeNames: 'authenticated' },
  8. 8];
  9. 9await this.ctx.meta.role.addRoleRightBatch({ atomClassName: 'party', roleRights });

Rules of Authorization Checking

Because atoms provide many features and states such as draft, simple workflow and public access will affect the checking rules of atom authorizations

Rules of Authorization Checking corresponding to different states of atoms is as follows:

Atom Action Draft Simple Workflow Public Access Normal
create - - - -
read only the creator has permission determine whether there is authorization for other actions. For example, if there is write permission, there must be read permission no need to check need to check
write、delete only the creator has permission need to check - need to check
custom actions - need to check - need to check
  • -: not support
  • create: only need to authorize on atom class, regardless of the atom data

Draft

Atom have two states: draft and normal

In the draft state, only the creator can perform the actions of write and delete

In the normal state, only authorized users can perform the corresponding authorized actions

Public Access

By default, atoms can only be queried if they are granted permission. CabloyJS also supports direct public access without permission. Just set the public of the atom class to 1

Simple Workflow

At present, CabloyJS implements the simple workflow mechanism

For example, we can design such a workflow:

1、The user creates a new article, which is in draft state, and can be read by self

2、The article submitted by the user cannot be accessed publicly at this time, but can be read by the users who have review permission

3、The article can only be accessed publicly after reviewed by users who have review permission

Authorization Checking

Authorization can be checked by middleware or API

Check by Middleware

CabloyJS uses the global middleware right to encapsulate the logic of authorization checking. It only needs to configure the corresponding middleware parameters on the API route

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

  1. 1{ method: 'post', path: 'test/atom/checkRightCreate', controller: testAtomRight, middlewares: 'test',
  2. 2 meta: { right: { type: 'atom', action: 1 } },
  3. 3},
  4. 4{ method: 'post', path: 'test/atom/checkRightRead', controller: testAtomRight, middlewares: 'test',
  5. 5 meta: { right: { type: 'atom', action: 2 } },
  6. 6},
  7. 7{ method: 'post', path: 'test/atom/checkRightWrite', controller: testAtomRight, middlewares: 'test',
  8. 8 meta: { right: { type: 'atom', action: 3 } },
  9. 9},
  10. 10{ method: 'post', path: 'test/atom/checkRightAction', controller: testAtomRight, middlewares: 'test',
  11. 11 meta: { right: { type: 'atom', action: 101 } },
  12. 12},
Name Description
right the global middleware right, which is enabled by default, only needs to configure parameters
type authorization type. here is atom authorization
action atom action code

Check by 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.meta.atomClass.get({ atomClassName: 'party' });
  3. 3// userIds
  4. 4const userIds = this.ctx.cache.mem.get('userIds');

action: create

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

action: read

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

action: write

  1. 1const checkRightWrites = [[ 'Tom', partyKey.atomId, true ], [ 'Tomson', partyKey.atomId, false ]];
  2. 2for (const [ userName, atomId, right ] of checkRightWrites) {
  3. 3 const res = await this.ctx.meta.atom.checkRightUpdate({
  4. 4 atom: { id: atomId, action: this.ctx.constant.module('a-base').atom.action.write },
  5. 5 user: { id: userIds[userName] },
  6. 6 });
  7. 7 assert.equal(!!res, right, userName);
  8. 8}

action: delete

  1. 1const checkRightDeletes = [[ 'Tom', partyKey.atomId, true ], [ 'Tomson', partyKey.atomId, false ]];
  2. 2for (const [ userName, atomId, right ] of checkRightDeletes) {
  3. 3 const res = await this.ctx.meta.atom.checkRightUpdate({
  4. 4 atom: { id: atomId, action: this.ctx.constant.module('a-base').atom.action.delete },
  5. 5 user: { id: userIds[userName] },
  6. 6 });
  7. 7 assert.equal(!!res, right, userName);
  8. 8}

custom actions

  1. 1// action: review(101)
  2. 2const checkRightActions_1 = [[ 'Tom', partyKey.atomId, false ], [ 'Jane', partyKey.atomId, true ]];
  3. 3for (const [ userName, atomId, right ] of checkRightActions_1) {
  4. 4 const res = await this.ctx.meta.atom.checkRightAction({
  5. 5 atom: { id: atomId, action: 101 },
  6. 6 user: { id: userIds[userName] },
  7. 7 });
  8. 8 assert.equal(!!res, right, userName);
  9. 9}