Entity Classification
Overview
Entities in the multi-dimensional security system are classified into five distinct security types. Each type determines how security is applied and which dimensions are checked during authorization.
Classification Types
A. Org-Scoped Only
Entities that belong to an organization and have no personal data component.
Characteristics
-
Contains an
orgIdfield (direct) -
Implements
OrganisationScopedinterface -
Security checked via organizational dimension only
-
Person dimension is not applicable
Examples
| Entity | Description |
|---|---|
Event |
A sporting event organized by a club or federation |
Organization |
The organization entity itself |
Venue |
A location owned/managed by an organization |
Competition |
A competition series run by an organization |
B. Person-Scoped Only
Entities containing personal data with no organizational component.
Characteristics
-
Contains a
personIdfield (direct) -
Implements
PersonScopedinterface -
Security checked via personal dimension only
-
Org dimension is not applicable
Examples
| Entity | Description |
|---|---|
PersonProfile |
Personal profile information (emergency contacts, medical info) |
PersonalBest |
Individual athlete’s personal best records |
MedicalInfo |
Medical history and information |
EmergencyContact |
Emergency contact details |
C. Composite-Scoped
Entities that implement both organizational and personal security interfaces. Each dimension is evaluated independently.
Characteristics
-
Contains both
orgIdandpersonId(direct or transitive) -
Implements both
OrganisationScopedANDPersonScopedinterfaces -
Each dimension is checked separately using its respective methods
-
Access requires passing BOTH dimension checks independently (AND logic)
-
Most restrictive security type
Examples
| Entity | Description |
|---|---|
EventEntry |
A person’s entry into an event (event→org, person→personal) |
RaceResult |
A person’s result in a race (race→event→org, person→personal) |
Payment |
Payment for an entry (event→org, person→payer) |
TeamMembership |
Person joining a team (team→org, person→member) |
Security Rule
User can access entity IF:
(user.accessibleOrgIds.contains(entity.orgId)
AND user has required org access level)
AND
(user.accessiblePersonIds.contains(entity.personId)
AND user has required person access level)
Implementation
@Entity
public class EventEntry implements OrganisationScoped, PersonScoped {
@Id
private Long id;
@ManyToOne(optional = false)
private Event event; // Provides orgId (transitive)
@Column(nullable = false)
private Long personId; // Direct person
@Override
public Long getOrgId() {
return event != null ? event.getOrgId() : null;
}
@Override
public void setOrgId(Long orgId) {
throw new UnsupportedOperationException("OrgId derived from event");
}
}
D. Transitive Org-Scoped
Child entities that inherit organizational access through a parent entity.
Characteristics
-
No direct
orgIdfield -
Access to org is via parent relationship
-
Implements helper method to get
orgIdfrom parent -
Org dimension checked via join to parent
Examples
| Entity | Parent Entity | Relationship |
|---|---|---|
Race |
Event |
Race belongs to Event, Event has orgId |
Heat |
Race |
Heat belongs to Race, Race→Event→orgId |
TeamMember |
Team |
Member belongs to Team, Team has orgId |
E. Transitive Person-Scoped
Child entities that inherit personal access through a parent entity.
Characteristics
-
No direct
personIdfield -
Access to person is via parent relationship
-
Implements helper method to get
personIdfrom parent -
Person dimension checked via join to parent
Examples
| Entity | Parent Entity | Relationship |
|---|---|---|
PersonAddress |
PersonProfile |
Address belongs to Profile, Profile has personId |
TrainingLog |
Athlete |
Log belongs to Athlete, Athlete has personId |
MedicalRecord |
Person |
Record belongs to Person |
Classification Decision Tree
Use this decision tree to classify your entities:
Does the entity belong to an organization?
│
├─ YES → Does it also relate to a specific person?
│ │
│ ├─ YES → DUAL-SCOPED (C)
│ │
│ └─ NO → Is orgId direct or via parent?
│ │
│ ├─ Direct → ORG-SCOPED ONLY (A)
│ │
│ └─ Via parent → TRANSITIVE ORG-SCOPED (D)
│
└─ NO → Does it relate to a specific person?
│
├─ YES → Is personId direct or via parent?
│ │
│ ├─ Direct → PERSON-SCOPED ONLY (B)
│ │
│ └─ Via parent → TRANSITIVE PERSON-SCOPED (E)
│
└─ NO → Not security-scoped (rare, admin-only entities)
Summary Table
| Type | Org Check | Person Check | Example |
|---|---|---|---|
A - Org-Scoped |
Direct orgId |
None |
Event, Venue |
B - Person-Scoped |
None |
Direct personId |
PersonProfile, MedicalInfo |
C - Composite-Scoped |
Direct or transitive |
Direct or transitive |
EventEntry, Payment |
D - Transitive Org |
Via parent |
None |
Race (via Event) |
E - Transitive Person |
None |
Via parent |
PersonAddress (via Profile) |
Multi-Level Transitive Access
Classification Best Practices
Keep It Simple
-
Prefer direct
orgId/personIdfields when possible -
Limit transitive depth to 2-3 levels maximum
-
Consider denormalizing for frequently accessed paths
Consistent Patterns
-
Always use the same parent relationship for org/person derivation
-
Document the access path clearly in entity comments
-
Use consistent naming (orgId, personId, not organizationId, etc.)
Performance Considerations
-
Transitive access requires JOINs - ensure proper indexing
-
Consider materialized views for complex multi-level access
-
Cache frequently accessed access paths
Testing Each Type
Each classification type should have dedicated tests:
// A - Org-Scoped
@Test
public void userCanAccessEventsInAccessibleOrgs()
// B - Person-Scoped
@Test
public void userCanAccessLinkedPersonProfiles()
// C - Composite-Scoped
@Test
public void userCanAccessEntriesWithBothOrgAndPersonAccess()
// D - Transitive Org
@Test
public void userCanAccessRacesViaEventOrg()
// E - Transitive Person
@Test
public void userCanAccessAddressesViaProfilePerson()
Next Steps
-
Understand the Security Resolution Algorithm
-
Review Core Entity definitions
-
See Entity Examples for each type