Broadleaf Microservices
  • v1.0.0-latest-prod

Key Admin User Service Components

Comparing Privileges

AdminPrivilegeService

The AdminPrivilegeService is mainly used as a utility for entity comparisons based on their collective privileges.

For example, this component helps with checking if an AdminUser entity is less restrictive than another given AdminUser based on restrictions alone or based on their overall privileges.

Definition of Being "Less Restrictive"

The AdminUser entity supports the concept of restrictions, flat permissions, flat roles, restricted roles, and restricted permissions. These concepts define where a user has access and what privileges they have. In some situations (such as guarding against privilege escalation), it is valuable to be able to compare users to determine whether one has access or privileges that the other doesn’t. If a user has access or privileges that another user doesn’t, we consider the former user to be "less restrictive" than the latter user.

Note
It is technically possible for both users to be "less restrictive" than each other if both have unique privileges or access that the other doesn’t.

When comparing the restrictiveness based on restrictions only, only the restrictions are taken into consideration. However, when comparing the restrictiveness based on privileges, all of their privileges are consolidated and evaluated as a whole.

To learn about the definitions of restrictions, flat permissions, etc. please check out Key Domain Concepts

Less Restrictive Based on Restrictions

An entity with no (empty) restrictions is not restricted in any way and can be considered "least restrictive" automatically. However, if an entity has one or more restrictions, it is exclusively restricted to only the specific targets within each specified restriction type.

For example, assume we have "entityX" with restrictions [{VENDOR: [vendorA, vendorB]}, {STORE: [storeA, storeB]}]. It is exclusively restricted to vendors vendorA and vendorB, and stores storeA and storeB. It is not allowed to access storeC or vendorC, nor is it allowed to access targets of any other arbitrary restriction type.

In order for an entity’s restrictions to be less restrictive than another entity, it must have access to restriction targets that the other entity does not.

If we have another "entityY" with restrictions [{VENDOR: [vendorA]}], it is exclusively restricted to vendor vendorA. It is not allowed to access vendorB or any other vendor besides vendorA, nor is it allowed to access any stores, nor is it allowed to access to targets of any other arbitrary restriction type. In this case, we would say that "entityX" is less restrictive than "entityY", as "entityX" is able to access items that "entityY" cannot. "entityY" is not less restrictive than "entityX", because "entityX" has access to all the restriction targets that "entityY" can.

Less Restrictive Based on Privileges

In order for an entity’s privileges to be less restrictive than another entity, it must have permissions (whether they come from flat permissions, flat roles, restricted roles, or restricted permissions) in a context/restriction that the other entity does not.

For example, assume we have a "userA" with restrictions on [vendorA, vendorC] with READ_PRODUCT "flat permission", and "restricted permission" of UPDATE_PRODUCT on [vendorC]. In this case, the user effectively has READ_PRODUCT permission on both vendorA and vendorC, and also UPDATE_PRODUCT permission on only vendorC.

Assume we have another "userB" with restrictions on [vendorA] with READ_PRODUCT "flat permission", and "restricted permission" of UPDATE_PRODUCT on [vendorA]. We would say that "userA" is less restrictive than "userB" because "userA" has READ_PRODUCT and UPDATE_PRODUCT on vendorC, which "userB" does not. Likewise, "userB" is also less restrictive than "userA" because "userB" has UPDATE_PRODUCT on vendorA, which "userA" does not.

Validation

When performing CRUD operations on AdminUser, they are first validated by their own ContextValidators, such as AdminUserContextValidator when performing operations on AdminUser. If the validations done by the ContextValidators are successful, they will then be validated by their EntityValidators, such as AdminUserValidator.

After successful validation done by the context validators, the entity’s EntityValidator will then validate the end state of the entity (after updates are applied but before the entity is persisted). These validations include checking against missing or invalid fields, or invalid references to other entities. For example, missing name is not allowed for AdminUser.

Validating Context

When a user is authenticated, the Authentication in the Spring Security Context is populated with various information about them, including what tenant/application access they have. This allows us to determine whether a CRUD operation in the requested ContextRequest is permitted by the authenticated user. When the authenticated user is in an invalid context, they should not be allowed to perform any CRUD operations on any entities. Similarly, when the user is in a valid context, they should not be allowed to perform any CRUD operations on entities outside that context.

For example, the authenticated user cannot modify another user, role, or permission in another tenant, since entities should not be accessible in different tenants, unless the user is in a global context. Furthermore, this logic applies to the referenced entities as well. To learn more about global vs. tenant context, please check out the Multi-Tenant Features Documentation

AdminUserContextValidator

The context validator that validates the context for the AdminUser entities.

Since the AdminUser entity can belong to a specific application, this validator also validates against invalid context for addition or removal of applications from a user. For example, the authenticated user must be in a tenant or global context to add/remove a user from an application.

Besides context validations, it validates against insufficient privileges for the currently authenticated user as well; the existing state of the entity must not already be less restrictive than the authenticated user. The restrictiveness check is based on their restrictions at the very least, if the entity’s restrictions are already less restrictive than the authenticated user’s restrictions prior the operation, the operation is not permitted.

Privileges Escalation Validation

To prevent privileges escalation for certain users, the AdminUser entity has the AdminUser#grantAnyAuthorityAllowed flag to determine whether the restrictiveness validation should also check against their overall privileges as well.

The restrictiveness check based on privileges will be skipped if the grantAnyAuthorityAllowed flag is true, since that means the authenticated user can grant any privileges it wants no matter what privileges it has. Otherwise, this validator will also make sure that the existing state of the entity is not already less restrictive than the authenticated user based on their overall privileges, which include all of their flat permissions, roles, restrictions, restricted roles, and restricted permissions.

Validating Entities

AdminUserValidator

After successful validations done by AdminUserContextValidator, this validator will then check the proposed AdminUser entity for creation or modification against invalid fields, invalid roles, and invalid permissions references.

Similar to AdminUserContextValidator, this validator also validates against insufficient privileges for the currently authenticated user based on restrictions, and their overall privileges (depends on the AdminUser#grantAnyAuthorityAllowed flag). The privileges escalation validation logic is the same, except for the state of the entities it validates against. The AdminUserContextValidator validates the privileges of the existing state of the entity, but this validator validates the privileges of the end state of the entity (after updates are applied but before the entity is persisted).

Hydrating

AdminPermissionHydrationService

This component is responsible for hydrating admin permission references with values from the authentication service.

AdminRoleHydrationService

This component is responsible for hydrating admin role references with values from the authentication service, as well as retrieving all the given roles along with their ancestors.

When retrieving given roles along with their ancestors, this component also validates against ancestry cycle formation and broken references.

AdminUserContextRequestHydrator - removed for release 2.1+

AdminUser Service’s implementation of DefaultContextRequestHydrator, which will hydrate the ContextRequest#application with the Application found from the local data store based on ContextRequest#applicationId.

Sandboxing Configuration

At the time of this writing, there’s no sandboxable entities in this service. If a sandboxable entity is introduced in this service, the following configurations should be added:

spring:
  cloud:
    stream:
      bindings:
        persistenceOutput:
            triggeredJobEventInputPurgeSandbox:
              group: adminuser-purge-sandbox
              destination: triggeredJobEvent
broadleaf:
   transitionrequest:
     enabled: true
   changesummary:
     notification:
       active: true
   tracking:
     sandbox:
       purge:
         enabled: true

See Sandboxing In Detail for more details.

Note
These configurations typically only affect the Granular Deployment model. For Min and Balanced deployements, these configurations are likely already added at the flexpackage-level configuration.