BaseUserGroupRightUtils.ktBaseUserGroupRightsDO object by checking the CSV ID fields against group membership and direct user ID matching. Used by both BaseUserGroupRight (#864) and TeamCalCache.isOwner(user, obj) / isOwner(userId, obj)Returns true if userId == obj.ownerId (both non-null). Simple identity check.
getAccessType(obj, userId): DataobjectAccessTypeThe core resolution method. Evaluates access in priority order:
NONE if obj or userId is nullOWNER if userId == obj.ownerIdfullAccessGroupIds and fullAccessUserIdsreadonlyAccessGroupIds and readonlyAccessUserIdsminimalAccessGroupIds and minimalAccessUserIdsNONEhasReadAccess(obj, userId, throwException?): BooleanReturns true if access type is READONLY, FULL, or OWNER. Otherwise returns false, or throws AccessException("access.exception.noReadAccess") if throwException is true.
hasWriteAccess(obj, userId, throwException?): BooleanReturns true if access type is FULL or OWNER. Otherwise returns false, or throws AccessException("access.exception.noWriteAccess").
hasAccess(obj, userId, operationType, throwException?)Routes to hasReadAccess or hasWriteAccess based on operationType.isWriteType.
hasAccess(obj, oldObj, userId, operationType, throwException?)For write operations, checks the old object first (allowing modification if user already had write access). Falls back to checking the new object. For read operations, checks oldObj ?: obj.
isMemberOfAny(groupIds, userIds, userId): BooleanPrivate helper that performs the actual group/user membership check:
groupIds is non-empty, checks UserGroupCache.getInstance().isUserMemberOfAtLeastOneGroup(userId, *groupIds)userIds contains the given userId, returns trueStringHelper.splitToLongObjects() to parse the CSV columns into Array<Long>BaseUserGroupRightsDO (CSV columns) │ ├─ StringHelper.splitToLongObjects() │ ├─ UserGroupCache.isUserMemberOfAtLeastOneGroup() ← groups └─ Direct userId comparison ← users │ ▼ DataobjectAccessType (OWNER / FULL / READONLY / MINIMAL / NONE) │ ▼ hasReadAccess / hasWriteAccess → Boolean
The object (Kotlin singleton) pattern was chosen because the access logic is purely functional—no state or dependencies, just computation. This allows it to be called from anywhere (services, caches, entity constructors) without injection overhead.
The tiered evaluation (owner → full → readonly → minimal → none) means the first match wins. If a user is both in the full-access group and the readonly group, they get FULL access. The oldObj parameter in hasAccess supports the pattern where permission to modify an object is checked against the pre-modification state—critical for update operations where the object's access lists may change as part of the edit.
UserGroupCache — singleton cache for group membership lookupsStringHelper.splitToLongObjects() — parses CSV ID strings to Array<Long>AccessException — thrown when throwException = true and access is denied868d6abb7 2025 -> 2026 63081666f Source file headers: 2024-> 2025. 4c04cfd65 MAJOR-CHANGE! Migration of integer id's to Long id's b6092df09 Copyright 2023 -> 2024 ab45d51fa Copyright 2001-2022 -> 2001-2023. 232a91c5a BaseUserGroupRightUtils implemented. WIP BankingPlugin and jobs-handling.