Atom Authorization

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

Authorization Record

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

Role AtomClass Actom Action
system party create

Resource Scope of Authorization

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

Role AtomClass Atom Action Resource 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

async addRoleRight({ roleId, atomClassId, action, scope })
Name Description
roleId RoleId to be authorized
atomClassId Atom Class Id
action Atom Action Code
scope Resource 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

// const roleRights = [
//   { roleName: 'cms-writer', action: 'create' },
//   { roleName: 'cms-writer', action: 'write', scopeNames: 0 },
//   { roleName: 'cms-writer', action: 'delete', scopeNames: 0 },
//   { roleName: 'cms-writer', action: 'read', scopeNames: 'authenticated' },
//   { roleName: 'cms-publisher', action: 'read', scopeNames: 'authenticated' },
//   { roleName: 'cms-publisher', action: 'write', scopeNames: 'authenticated' },
//   { roleName: 'cms-publisher', action: 'publish', scopeNames: 'authenticated' },
//   { roleName: 'root', action: 'read', scopeNames: 'authenticated' },
// ];
async addRoleRightBatch({ module, atomClassName, atomClassIdParent = 0, 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/module/test-party/backend/src/service/version.js

// add role rights
const roleRights = [
  { roleName: 'system', action: 'create' },
  { roleName: 'system', action: 'write', scopeNames: 0 },
  { roleName: 'system', action: 'delete', scopeNames: 0 },
  { roleName: 'system', action: 'read', scopeNames: 'authenticated' },
  { roleName: 'system', action: 'review', scopeNames: 'authenticated' },
];
await 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/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 } },
},
{ method: 'post', path: 'test/atom/checkRightAction', controller: testAtomRight, middlewares: 'test',
  meta: { right: { type: 'atom', action: 101 } },
},
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/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');

action: 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);
}

action: 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);
}

action: write

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

action: delete

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

custom actions

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