Skip to content

Architecture

  1. Backend is the single source of truth — the frontend renders what the backend permits; it never decides.
  2. Three-tier permissions — feature access, action rights, field visibility.
  3. DENY wins; absence denies — conflict resolution is exhaustive and fail-closed.
  4. 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.
  5. Ports & adapters — services depend on persistence ports; Spring Data JPA is the default adapter, not a commitment.
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, enums

The auto-configuration deliberately scans only IAM-owned packages and gates every bean on conditions — a host’s context stays clean.

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).

  1. Collect direct roles, group roles, and the role hierarchy (parents).
  2. Merge all permissions; apply DENY-wins and most-specific-wins.
  3. 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.

  • Row-level: Hibernate @FilterDef/@Filter auto-scopes reads from a request-scoped TenantContext; an Interceptor write-guard rejects cross-tenant writes.
  • Schema: Hibernate SCHEMA multi-tenancy — CurrentTenantIdentifierResolver + a connection provider that switches Connection.schema on checkout and resets on release.
  • Database: a routing MultiTenantConnectionProvider over a IamTenantDataSourceRegistry (pluggable; properties-backed default), failing fast on unknown tenants.

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.