Architecture
Principles
Section titled “Principles”- Backend is the single source of truth — the frontend renders what the backend permits; it never decides.
- Three-tier permissions — feature access, action rights, field visibility.
- DENY wins; absence denies — conflict resolution is exhaustive and fail-closed.
- Every capability opt-out with a safe default — config toggles select strategies; disabled means a no-op bean, never a missing one; every default loses to a host-provided bean.
- Ports & adapters — services depend on persistence ports; Spring Data JPA is the default adapter, not a commitment.
Modules
Section titled “Modules”iam-spring-boot-starter deps-only drop-in (what hosts depend on) └─ iam-spring-boot-autoconfigure @AutoConfiguration + dev-mode secrets └─ iam-rest controllers, OpenAPI, error advice └─ iam-core entities, services, resolution, security, migrations, SPI └─ iam-api Spring-free contract: @RequiresPermission/@FieldFiltered/@Audited, DTOs, enumsThe auto-configuration deliberately scans only IAM-owned packages and gates every bean on conditions — a host’s context stays clean.
Data model
Section titled “Data model”erDiagram TENANT ||--o{ USER : has TENANT ||--o{ ROLE : has TENANT ||--o{ RESOURCE : has USER }o--o{ ROLE : "user_roles (scope?)" USER }o--o{ GROUP : user_groups GROUP }o--o{ ROLE : group_roles ROLE ||--o{ ROLE : "parent hierarchy" ROLE }o--o{ PERMISSION : role_permissions RESOURCE ||--o{ RESOURCE_FIELD : fields RESOURCE }o--o{ ACTION : resource_actions PERMISSION }o--|| RESOURCE : on PERMISSION }o--|| ACTION : for PERMISSION }o--o| RESOURCE_FIELD : "field (optional)"All tables are iam_-prefixed; migrations are vendor-partitioned and run through IAM’s own
dedicated Flyway instance (history table iam_flyway_history — the host’s spring.flyway.*
stays untouched; guide).
Permission resolution
Section titled “Permission resolution”- Collect direct roles, group roles, and the role hierarchy (parents).
- Merge all permissions; apply DENY-wins and most-specific-wins.
- Produce the resolved object (features → actions → field access) and cache it.
The resolved object is the contract between backend and frontend — one shape consumed by
@RequiresPermission, PermissionChecker, and the React library alike.
Tenancy internals
Section titled “Tenancy internals”- Row-level: Hibernate
@FilterDef/@Filterauto-scopes reads from a request-scopedTenantContext; anInterceptorwrite-guard rejects cross-tenant writes. - Schema: Hibernate SCHEMA multi-tenancy —
CurrentTenantIdentifierResolver+ a connection provider that switchesConnection.schemaon checkout and resets on release. - Database: a routing
MultiTenantConnectionProviderover aIamTenantDataSourceRegistry(pluggable; properties-backed default), failing fast on unknown tenants.
Decision records
Section titled “Decision records”The repository carries ADRs documenting the why: modularization (ADR-001) and the pluggability/portability programme (ADR-002 — cache abstraction, permission API, persistence ports, tenancy isolation, database portability), including honest notes on what was deferred and what would trigger it.