UTMS NG Backend (Spring Boot)
Production-ready Spring Boot 3.x backend for user tenancy, RBAC, maker-checker workflow, module management, Redis caching/session support, ActiveMQ eventing, and i18n.
Table of Contents
- Project Overview
- Stack and Runtime Versions
- High-Level Architecture
- Repository and Package Layout
- Getting Started
- Configuration
- Security & AuthN/AuthZ
- Multi-Tenancy
- Workflow Engine
- Module System
- API Documentation
- Eventing and Messaging
- Persistence Model
- i18n and Error Handling
- Observability
- Sequence Diagrams
- Useful Commands
- Contributing Notes
Project Overview
The system is organized into modular packages and service layers:
api: REST-facing controllers.auth: authentication, authorization, JWT, LDAP integration, token handling, and user-role primitives.core: cross-cutting concerns (errors, base entities, audit, caching, i18n, DB config).tenant: tenant context and tenant isolation filters.workflow: maker-checker workflow engine.module: pluggable feature module registry.messaging: ActiveMQ producer/consumer for async post-approval actions.
Stack and Runtime Versions
The project runs on Java 17 and uses Spring Boot starter dependencies.
- Java 17+
- Spring Boot 3.3.5
- Spring Security
- Spring Data JPA + Hibernate
- PostgreSQL
- Redis
- ActiveMQ
- Maven
- SpringDoc OpenAPI
Main build and dependency file:
High-Level Architecture
The application follows request/response flow with cross-cutting servlet filters:
TenantFilterresolves tenant ID fromX-Tenant-Id.JwtAuthenticationFilterauthenticates bearer tokens when present.- Method-level security checks RBAC/permission requirements.
- Business services apply transaction boundaries and audit/logging.
- Workflow events are published to ActiveMQ on approval completion.
Core architectural file references:
- application entrypoint
- security config
- openapi config
- tenant context filter
- tenant hibernate filter
- tenant entity listener
- base entity and auditing
- audit trail
Repository and Package Layout
Top-level structure:
src/main/java/id/iptek/utmssrc/main/resourcesdocs(documentation)docker-compose.ymlpom.xml
Important package-level references:
Getting Started
Prerequisites
- JDK 17
- PostgreSQL 16
- Redis 7+
- ActiveMQ 5.18+
- Maven
- PowerShell (for local commands in this environment)
Local run with Docker
- Start infrastructure:
docker compose up -d
- Build the application image and run as in compose:
docker compose up --build -d
- Access services:
- Backend:
http://localhost:9191 - Swagger UI:
http://localhost:9191/swagger-ui.html - API docs:
http://localhost:9191/v3/api-docs - Postgres:
localhost:5432 - Redis:
localhost:6379 - ActiveMQ admin:
http://localhost:8161
Configuration
Profiles are defined in:
Common config sections:
- datasource: PostgreSQL connection
- data.redis: Redis host/port and cache config
- activemq: broker and credentials
- app.ldap: optional LDAP integration (disabled by default)
- app.security.login.*: login brute-force thresholds
- app.seed.enabled: bootstrap sample data flag
Brute-force defaults for login:
- max failed attempts:
app.security.login.max-failed-attempts - attempt window (seconds):
app.security.login.failed-attempt-window-seconds - lockout window (seconds):
app.security.login.lockout-duration-seconds
Single-session login option:
app.security.single-login.enabled(defaultfalse)true= user can only have one active session at a time; new login invalidates previous access/refresh session.
Security & AuthN/AuthZ
Login and JWT
- login endpoint:
POST /api/auth/login - refresh endpoint:
POST /api/auth/refresh - logout endpoint:
POST /api/auth/logout - JWT utility: JwtService
- JWT principal adapter: UserPrincipal
- token filter: JwtAuthenticationFilter
- auth service: AuthService
- rate limit lockout service: LoginThrottleService
- token blacklist: TokenBlacklistService
- refresh token entity: RefreshToken
RBAC model
- Roles are prefixed with
ROLE_in authorities viaUserPrincipal. - Permissions are loaded from
Role -> Permissionand exposed as authorities directly. - Default protected role/permission checks use
@PreAuthorize. - Common checks:
hasRole('ADMIN')hasAuthority('WORKFLOW_APPROVE')hasAuthority('USER_MANAGE')hasAuthority('ROLE_MANAGE')
Optional LDAP
LDAP can be enabled without code changes using profile configuration.
app.ldap.enabled=trueto switch it on.- LDAP-specific properties are in application.yml.
- Provider wiring is in LdapAuthConfig.
Multi-Tenancy
Tenant is always provided by:
X-Tenant-Idheader via TenantFilter- JWT claim
tenantvia JwtService
Tenant context is thread-bound:
- set by
TenantContextand used by services and entities.
Tenant isolation strategy:
- Hibernate
tenantFilteris defined on tenant-scoped entities. - TenantHibernateFilter enables filter per request.
BaseEntityholdstenant_idfor all shared tables.TenantServicevalidates active tenant with cache.
Tenant validation for active tenants:
Workflow Engine
Workflow is required for user/role management operations and available as a first-class service.
- workflow service: ApprovalWorkflowService
- approval controller: ApprovalWorkflowController
- workflow entities:
- ApprovalRequest
- ApprovalStep
- ApprovalHistory
- approval DTOs: CreateApprovalRequest, ApprovalActionRequest, ApprovalResponse
Workflow state progression:
- Maker creates request with required steps.
- Checker role per step enforces which role can approve.
- Each step is persisted as
PENDING. - Approve action updates step and request progress.
- Reject action sets request to
REJECTED. - Final approval publishes
ApprovalCompletedEventto ActiveMQ and user-role changes are applied by consumer.
Module System
- module domain: SystemModule
- module registry service: ModuleRegistryService
- module contract: Module
- sample module: NotificationModule
Module operations:
- list:
GET /api/modules - toggle by code:
POST /api/modules/{code}/toggle
API Documentation
Swagger/OpenAPI:
- UI:
http://localhost:9191/swagger-ui.html - JSON spec:
http://localhost:9191/v3/api-docs - openapi settings: OpenApiConfig
Endpoints
Auth endpoints:
- POST
/api/auth/login - POST
/api/auth/refresh - POST
/api/auth/logout
Tenant endpoint:
- GET
/api/tenant/context
User endpoint:
- GET
/api/users/me - POST
/api/users/management/requests/create - POST
/api/users/management/requests/update-roles
Role endpoints:
- POST
/api/roles/management/requests/create - POST
/api/roles/management/requests/update-permissions
Workflow endpoints:
- POST
/api/workflow/request - POST
/api/workflow/{id}/approve - POST
/api/workflow/{id}/reject
Module endpoints:
- GET
/api/modules - POST
/api/modules/{code}/toggle
Audit endpoints:
- GET
/api/audit?limit=50
Health endpoint:
- GET
/actuator/health
Swagger-safe quick sample JSON:
{
"username": "maker",
"password": "Passw0rd!"
}
{
"resourceType": "USER_MANAGEMENT",
"resourceId": "sample-user",
"payload": "{\"operation\":\"CREATE_USER\",\"username\":\"alice\"}",
"requiredSteps": 1
}
{
"username": "admin",
"roleCodes": ["ADMIN"]
}
Mandatory headers
- Tenant:
X-Tenant-Id: acme- Locale:
Accept-Language: en-USorid-ID- Authorization:
Authorization: Bearer <jwt>
Eventing and Messaging
ActiveMQ integration:
- producer: ApprovalEventProducer
- consumer: ApprovalEventConsumer
- event payload: ApprovalCompletedEvent
- queue name:
approval.completed.queue
Post-approval process:
- event published when an approval request reaches
APPROVED. - consumer invokes UserRoleManagementService.applyApprovedRequest.
Persistence Model
Naming and schema strategy
- security tables use
sec_prefix. - workflow/system/audit tables use
sys_prefix. - This is maintained in all entities and confirmed in the schema file.
Database schema reference:
Core entities:
- Tenant: Tenant
- RBAC:
- User: User
- Role: Role
- Permission: Permission
- Refresh token: RefreshToken
- Workflow:
- ApprovalRequest: ApprovalRequest
- ApprovalStep: ApprovalStep
- ApprovalHistory: ApprovalHistory
- Module: SystemModule
- Audit: AuditTrail
Repositories
- PermissionRepository
- RoleRepository
- UserRepository
- RefreshTokenRepository
- AuditTrailRepository
- Workflow repositories
i18n and Error Handling
Message bundles:
- default (en_US): messages.properties
- indonesia locale (id_ID): messages_id.properties
Message resolution helper:
Global errors:
- GlobalExceptionHandler
- app exception model:
- AppException
Observability
- Actuator:
- health/info endpoints via config in application.yml
- Audit logger:
- AuditLoggingAspect
- persistent audit trail records:
- AuditTrailService
- Audit API
Sequence Diagrams
Full controller interaction diagrams are available in:
Useful Commands
Build only:
mvn -q -DskipTests compile
Run tests:
mvn test
Run locally (default profile):
$env:SPRING_PROFILES_ACTIVE="local"; mvn spring-boot:run
Run dev profile:
$env:SPRING_PROFILES_ACTIVE="dev"; mvn spring-boot:run
Run prd profile:
$env:SPRING_PROFILES_ACTIVE="prd"; mvn spring-boot:run
Contributing Notes
Data seeding:
- enabled in
devandlocalvia profile/setting. - seed source: DataSeeder.
- to add bootstrap users/roles/permissions, modify this component intentionally.
Extending modules:
- implement Module
- register as Spring component
- expose behavior in toggle handlers.
Extending API:
- add DTOs under
auth|workflow|module|api - add service under domain package
- create controller endpoint and secure with
@PreAuthorize - update docs in this file and docs/sequence-diagrams.md.