Files
UTMS-NG-BE/README.md
2026-04-21 06:25:33 +07:00

446 lines
19 KiB
Markdown

# 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 <jwt>`
## 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).