Inherit access group entity constraints down a one-to-many association path

Hello!

Let’s say we have the following entity model (similar to that of the SaaS demo Studio project):

Client  - 1..* - Customer
Customer - 1..* - Order
Order - 1..* - OrderItem
Customer - 1..* - Address
... and so on

If a user is a member of an access group which limits visibility of the Client entity to one with a specific ID, is there a way to get that constraint to implicitly propagate down the chain of one-to-many associations, so that the user can only see Customers which belong to their permitted Client, and only see Orders and Addresses which belong to Customers which belong to their permitted client, etc.

I know we can set up individual constraints for each entity, but this would be time-consuming and error-prone. It would be much easier if we could just set up the constraint on the highest-level entity and have it automatically apply to all associated entities along the one-to-many (and, of course, one-to-one) association path.

Hi,

currently i don’t think this is really possible. I created this idea of defining constrains on @MappedSuperclass entities: https://www.cuba-platform.com/discuss/t/define-access-group-constraints-on-mappedsuperclasses

The implementation currently is scheduled for 6.5.

Until then, one approach to enforce something like this would be that you generate these constraints when the application server starts.

You can define a AppContextListener and implement the applicationStarted method.

Within this method you can search for all Entities that are sub entities of the TenantEntity (in my example) and create a constraint entry in the database for a certain security group (that you configure via application properties etc. where the constraints should be added.

Here’s the idea in pseudo code:


class MyConstraintAppContextListener implements AppContextListener {

 @Inject
 Metadata metadata

 @Override
 void applicationStarted() {
  def tenantEntitiesClasses = findTenantEntities()
  tenantEntitiesClasses.each {
   createConstraintForTenantEntityClass(it)
  }
 }

 void createConstraintForTenantEntityClass(MetaClass m) {
  // def c = metadata.create(Constraint)
  // c.entityName = m.name
  // c.whereClause = '{e}.tenant = :session$tenantId'
  // addConstraintToGroup(c)
 }

 def findTenantEntities() {
  metadata.session.classes.collect {
   if (TenantEntity.class.isAssignableFrom(metaClass.javaClass)) {
    return metaClass
   }
  }
 }

}

`

This way the constraints get added at application start up and you can never forget anything. It has the downside though that this will enforce this (as long as you don’t define some clever mechanism to find out if a user changed anything) and it is pretty static towards a particular sec$Group that it expects.

But that might work.

Bye
Mario

1 Like

What do you mean by “to have to do that for every entity”? Since TenantEntity is the superclass of all business entities, the code has just to be written once, and not for every new entity.

Hi Mario,

Thanks for your response; that’s pretty much the approach I’m currently taking. It just feels a bit heavy-handed to have to do that for every entity.

Kind regards,

David