Access Control

Imagine your Entity Product can be accessible by certain users. You can define a property that indicate it is accessible or not.

export default class Product extends Model {

    id = this.field(PrimaryKeyType)

    isAccessibleByUserId = Product.compute((parent, userId: number): CFReturn<boolean> => {
        // here we use a query builder to query another Model 'UserProduct' to find out if the product can be accessed by a user
        return new DScalar(context => context.dataset()
          .from( UserProduct.datasource('up') )
          .where({
            'up.userId': userId,
            'up.productId': parent.id
          }).exists()
        )
    })
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Then you can use it like a function.

  const currentUserId = 1

  let targetProducts = await Product.find({
    where: ({root, And}) => And(
      { isActive: true },
      root.isAccessibleByUserId(currentUserId)  // the function call return a boolean Scalar
    )
  })

1
2
3
4
5
6
7
8
9

Role-Based Access Control

If you find some logics are often repeated on many Models. It is suggested to create a util function. Let say you want all Entities has Role-Based Access Control. Here are the related tables Role, User, RoleEntity.

User  
- id
- roleId

Role
- id

RoleEntity
- roleId
- entityName   (Model which that role can access)
1
2
3
4
5
6
7
8
9
10

We define a Model class as a base class of the other models you want to be protected with Access Control

//rbacModel.ts
export default class RBACModel extends Model {

    static propertyOfEditableByUserId(entityName: string){

      return Product.compute((parent, userId: number): CFReturn<boolean> => {
          // here we use a query builder to query another Model 'UserProduct' to find out if the product can be accessed by a user
          return new DScalar(context => context.dataset()
            .from( Role.datasource('role') )
            .innerJoin( User.datasource('user'), ({role, user}) => user.roleId.equals(role.id))
            .innerJoin( RoleEntity.datasource('re'), ({role, re}) => role.id.equals(re.roleId) )
            .where( ({re}) => re.entityName.equals( entityName ))
            .exists()
          )
      })
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//product.ts
export default class Product extends RBACModel {
  id = this.field(PrimaryKeyType)

  //use the static method to create a property
  isEditableByUserId = Product.propertyOfEditableByUserId('product')
}
1
2
3
4
5
6
7
  const currentUserId = 1

  let products = await Product.find({
    where: ({root}) => 
      root.isEditableByUserId(currentUserId)  // the function call return a boolean Scalar
  })
1
2
3
4
5
6