# 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](#project-overview) - [Stack and Runtime Versions](#stack-and-runtime-versions) - [High-Level Architecture](#high-level-architecture) - [Repository and Package Layout](#repository-and-package-layout) - [Getting Started](#getting-started) - [Configuration](#configuration) - [Security & AuthN/AuthZ](#security--authnauthz) - [Multi-Tenancy](#multi-tenancy) - [Workflow Engine](#workflow-engine) - [Module System](#module-system) - [API Documentation](#api-documentation) - [Eventing and Messaging](#eventing-and-messaging) - [Persistence Model](#persistence-model) - [i18n and Error Handling](#i18n-and-error-handling) - [Observability](#observability) - [Sequence Diagrams](#sequence-diagrams) - [Useful Commands](#useful-commands) - [Contributing Notes](#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: - [pom.xml](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/pom.xml) ## High-Level Architecture The application follows request/response flow with cross-cutting servlet filters: 1) `TenantFilter` resolves tenant ID from `X-Tenant-Id`. 2) `JwtAuthenticationFilter` authenticates bearer tokens when present. 3) Method-level security checks RBAC/permission requirements. 4) Business services apply transaction boundaries and audit/logging. 5) Workflow events are published to ActiveMQ on approval completion. Core architectural file references: - [application entrypoint](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/UtmsNgBeApplication.java) - [security config](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/config/SecurityConfig.java) - [openapi config](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/config/OpenApiConfig.java) - [tenant context filter](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/tenant/TenantFilter.java) - [tenant hibernate filter](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/tenant/TenantHibernateFilter.java) - [tenant entity listener](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/domain/TenantEntityListener.java) - [base entity and auditing](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/domain/BaseEntity.java) - [audit trail](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/audit/domain/AuditTrail.java) ## Repository and Package Layout Top-level structure: - `src/main/java/id/iptek/utms` - `src/main/resources` - `docs` (documentation) - `docker-compose.yml` - `pom.xml` Important package-level references: - [api controllers](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/api) - [auth module](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth) - [core module](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core) - [tenant module](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/tenant) - [workflow module](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow) - [module system](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/module) - [messaging](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/messaging) ## Getting Started ### Prerequisites - JDK 17 - PostgreSQL 16 - Redis 7+ - ActiveMQ 5.18+ - Maven - PowerShell (for local commands in this environment) ### Local run with Docker 1) Start infrastructure: ```shell docker compose up -d ``` 2) Build the application image and run as in compose: ```shell docker compose up --build -d ``` 3) 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: - [application.yml](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/resources/application.yml) - [application-dev.yml](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/resources/application-dev.yml) - [application-prd.yml](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/resources/application-prd.yml) - [application-local.yml](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/resources/application-local.yml) - [Docker compose](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/docker-compose.yml) 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` (default `false`) - `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](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/security/JwtService.java) - JWT principal adapter: [UserPrincipal](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/security/UserPrincipal.java) - token filter: [JwtAuthenticationFilter](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/security/JwtAuthenticationFilter.java) - auth service: [AuthService](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/service/AuthService.java) - rate limit lockout service: [LoginThrottleService](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/service/LoginThrottleService.java) - token blacklist: [TokenBlacklistService](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/service/TokenBlacklistService.java) - refresh token entity: [RefreshToken](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/domain/RefreshToken.java) ### RBAC model - Roles are prefixed with `ROLE_` in authorities via `UserPrincipal`. - Permissions are loaded from `Role -> Permission` and 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=true` to switch it on. - LDAP-specific properties are in [application.yml](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/resources/application.yml). - Provider wiring is in [LdapAuthConfig](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/config/LdapAuthConfig.java). ## Multi-Tenancy Tenant is always provided by: - `X-Tenant-Id` header via [TenantFilter](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/tenant/TenantFilter.java) - JWT claim `tenant` via [JwtService](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/security/JwtService.java) Tenant context is thread-bound: - set by `TenantContext` and used by services and entities. Tenant isolation strategy: - Hibernate `tenantFilter` is defined on tenant-scoped entities. - [TenantHibernateFilter](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/tenant/TenantHibernateFilter.java) enables filter per request. - `BaseEntity` holds `tenant_id` for all shared tables. - `TenantService` validates active tenant with cache. Tenant validation for active tenants: - [TenantService](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/tenant/TenantService.java) - [TenantRepository](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/tenant/TenantRepository.java) ## Workflow Engine Workflow is required for user/role management operations and available as a first-class service. - workflow service: [ApprovalWorkflowService](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/service/ApprovalWorkflowService.java) - approval controller: [ApprovalWorkflowController](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/controller/ApprovalWorkflowController.java) - workflow entities: - [ApprovalRequest](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/domain/ApprovalRequest.java) - [ApprovalStep](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/domain/ApprovalStep.java) - [ApprovalHistory](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/domain/ApprovalHistory.java) - approval DTOs: [CreateApprovalRequest](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/dto/CreateApprovalRequest.java), [ApprovalActionRequest](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/dto/ApprovalActionRequest.java), [ApprovalResponse](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/dto/ApprovalResponse.java) 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 `ApprovalCompletedEvent` to ActiveMQ and user-role changes are applied by consumer. ## Module System - module domain: [SystemModule](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/module/domain/SystemModule.java) - module registry service: [ModuleRegistryService](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/module/service/ModuleRegistryService.java) - module contract: [Module](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/module/service/Module.java) - sample module: [NotificationModule](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/module/service/NotificationModule.java) 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](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/config/OpenApiConfig.java) ### 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: ```json { "username": "maker", "password": "Passw0rd!" } ``` ```json { "resourceType": "USER_MANAGEMENT", "resourceId": "sample-user", "payload": "{\"operation\":\"CREATE_USER\",\"username\":\"alice\"}", "requiredSteps": 1 } ``` ```json { "username": "admin", "roleCodes": ["ADMIN"] } ``` ### Mandatory headers - Tenant: - `X-Tenant-Id: acme` - Locale: - `Accept-Language: en-US` or `id-ID` - Authorization: - `Authorization: Bearer ` ## Eventing and Messaging ActiveMQ integration: - producer: [ApprovalEventProducer](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/messaging/ApprovalEventProducer.java) - consumer: [ApprovalEventConsumer](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/messaging/ApprovalEventConsumer.java) - event payload: [ApprovalCompletedEvent](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/messaging/ApprovalCompletedEvent.java) - queue name: `approval.completed.queue` Post-approval process: - event published when an approval request reaches `APPROVED`. - consumer invokes [UserRoleManagementService.applyApprovedRequest](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/service/UserRoleManagementService.java). ## 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: - [schema.sql](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/resources/db/schema.sql) Core entities: - Tenant: [Tenant](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/tenant/Tenant.java) - RBAC: - User: [User](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/domain/User.java) - Role: [Role](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/domain/Role.java) - Permission: [Permission](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/domain/Permission.java) - Refresh token: [RefreshToken](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/domain/RefreshToken.java) - Workflow: - ApprovalRequest: [ApprovalRequest](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/domain/ApprovalRequest.java) - ApprovalStep: [ApprovalStep](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/domain/ApprovalStep.java) - ApprovalHistory: [ApprovalHistory](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/domain/ApprovalHistory.java) - Module: [SystemModule](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/module/domain/SystemModule.java) - Audit: [AuditTrail](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/audit/domain/AuditTrail.java) ### Repositories - [PermissionRepository](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/repository/PermissionRepository.java) - [RoleRepository](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/repository/RoleRepository.java) - [UserRepository](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/repository/UserRepository.java) - [RefreshTokenRepository](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/auth/repository/RefreshTokenRepository.java) - [AuditTrailRepository](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/audit/repository/AuditTrailRepository.java) - [Workflow repositories](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/workflow/repository) ## i18n and Error Handling Message bundles: - default (en_US): [messages.properties](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/resources/i18n/messages.properties) - indonesia locale (id_ID): [messages_id.properties](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/resources/i18n/messages_id.properties) Message resolution helper: - [MessageResolver](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/i18n/MessageResolver.java) Global errors: - [GlobalExceptionHandler](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/exception/GlobalExceptionHandler.java) - app exception model: - [AppException](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/exception/AppException.java) ## Observability - Actuator: - health/info endpoints via config in [application.yml](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/resources/application.yml) - Audit logger: - [AuditLoggingAspect](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/config/AuditLoggingAspect.java) - persistent audit trail records: - [AuditTrailService](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/audit/service/AuditTrailService.java) - [Audit API](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/api/AuditController.java) ## Sequence Diagrams Full controller interaction diagrams are available in: - [docs/sequence-diagrams.md](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/docs/sequence-diagrams.md) ## Useful Commands Build only: ```shell mvn -q -DskipTests compile ``` Run tests: ```shell mvn test ``` Run locally (default profile): ```shell $env:SPRING_PROFILES_ACTIVE="local"; mvn spring-boot:run ``` Run dev profile: ```shell $env:SPRING_PROFILES_ACTIVE="dev"; mvn spring-boot:run ``` Run prd profile: ```shell $env:SPRING_PROFILES_ACTIVE="prd"; mvn spring-boot:run ``` ## Contributing Notes Data seeding: - enabled in `dev` and `local` via profile/setting. - seed source: [DataSeeder](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/core/config/DataSeeder.java). - to add bootstrap users/roles/permissions, modify this component intentionally. Extending modules: - implement [Module](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/src/main/java/id/iptek/utms/module/service/Module.java) - 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](/D:/Projects/Personal/IPTEK/utms-ng/utms-ng-be/docs/sequence-diagrams.md).