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
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
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
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
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
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
2
3
4
5
6