Dokumen ini memetakan alur yang benar-benar berjalan pada source saat ini untuk fitur Data Masking, mulai dari pembuatan dan rotasi Data Access Key (token), konfigurasi dynamic field yang sensitif, penyisipan unmasking (dekripsi) untuk search filter dari request, hingga proses masking selektif (enkripsi) pada payload response. Dokumen ini adalah baseline perilaku current, bukan desain target.
| Area | Status | Batas Verifikasi |
|---|---|---|
| Service flow dan branching | SOURCE-TRACED | Ditelusuri dari TransactionMaskService dan dependensinya |
| Endpoint dan header | SOURCE-TRACED | Ditelusuri dari penggunaan anotasi header X-Data-Access-Key di controllers |
| Database behavior | SOURCE-TRACED | Berdasarkan token user dan config entity behavior |
| Runtime behavior | NOT RUNTIME-VERIFIED | Tidak menjalankan aplikasi atau integration test |
| Production behavior | NOT PRODUCTION-VERIFIED | Tidak memeriksa cron setting dan keys environment production |
| Concern | Komponen Current | Tanggung Jawab |
|---|---|---|
| Masking orchestration | TransactionMaskService |
Menangkap map payload, menguji token, meloop active sensitive config, dan mengganti nilai menjadi cipher atau fallback. |
| Access Token lifecycle | DataAccessKeyService |
Menginisialisasi dummy key, memvalidasi input user vs database, menyimpan cache validasi, dan melakukan cron harian rotasi token. |
| Encryption engine | EncryptionService |
Melakukan operasi kriptografi AES/GCM/NoPadding menggunakan active KeyService. Menelan error decrypt pada unmasking filter. |
| Sensitive fields registry | SensitiveFieldConfigService |
Menyimpan field mana saja yang dianggap sensitif. Memvalidasi field baru terhadap domain class yang didukung. |
Token (Data Access Key) setiap user disimpan dan dirotasi di dalam database secara otomatis. Mekanisme validasi memiliki fallback terhadap format key legacy.
[!DANGER] Cache Invalidation Delay
validationCachemenggunakanConcurrentHashMapdengan TTL 5 menit (CACHE_TTL_MS), namun current behavior tidak membersihkan cache spesifik user jika role atau token mereka dicabut (misal: deactivate user) di pertengahan jalan, kecuali ketika Global Scheduled Rotation berjalan. Token yang sudah ter-cache akan terus di-honor hingga 5 menit berlalu.
Mekanisme generation token current membuat random token sepanjang 64 karakter dari gabungan 2 UUID yang di-strip -nya.
SensitiveFieldConfigService mengatur field apa saja yang disembunyikan.
[!DANGER] Tightly Coupled Static Validation
Validasi field asertif pada variabel statis
VALID_FIELDSyang hanya me-load attributes dariExistingCurrTrans.class.getDeclaredFields(). Jika current app memiliki entity lain yang digunakan dalam response (misalnyaAlertCaseatauAcquirerAlert) dan entity tersebut memiliki nama field unik yang butuh di-mask, current behavior menolak registrasinya. Hanya field dengan nama sama dengan milikExistingCurrTransatau field dalam objekaddtDatayang bisa ditambahkan.
Semua entry point pencarian (/search) melakukan pemanggilan ke unmaskSearchFilters untuk menterjemahkan kembali ciphertext dari client menjadi plaintext agar database criteria builder bisa berjalan (misalnya query exact match pada No Rekening).
[!DANGER] Silent Fallback pada Dekripsi Filter
decryptIfEncryptedmenangkapException e(General Exception) dan me-return original value secara pasif. Ini artinya sistem tidak bisa membedakan mana "client dengan sengaja mengirim plaintext" versus "client mengirim random base64 yang gagal decrypt". Keduanya akan lolos ke business logic dan diproses sebagai kriteria filter.
Jika token invalid atau absent, proses mutasi map data berjalan (replace original value).
[!DANGER] Type Mismatch di JSON Payload
Jika field yang dikonfigurasi sebagai sensitif aslinya berjenis Number atau Boolean (contoh:
amount,isFraud), current behavior memaksakan field tersebut menjadi String berbunyi"[PROTECTED]". Ini akan langsung mengubah skema response API (dari integer ke string) dan berpotensi memunculkan error unmarshal/deserialization pada sistem UI / Client.
| Skenario | Masking Payload | Search Filter (Unmask) | Assignment/State Lain | Next Step |
|---|---|---|---|---|
Request tanpa X-Data-Access-Key |
Masking berjalan aktif | Dekripsi di filter tetap diupayakan | Data disamarkan | UI menampilkan Cipher |
| Token valid tapi cache miss | Masking di-skip (Plaintext) | Filter di-decrypt / lewat | Cache terisi timestamp baru | UI menampilkan Plaintext |
| Token valid dan cache hit | Masking di-skip (Plaintext) | Filter di-decrypt / lewat | DB tidak disentuh untuk user key | UI menampilkan Plaintext |
| Token kadaluwarsa/diubah via Cron | Masking berjalan (ditolak) | Filter tetap bisa unmask (jika ciphertext valid) | Data disamarkan | Client harus request key baru (SSE) |
| Client mengirim Plaintext di filter | Masking bergantung Token | Fallback raw value di controller | Query DB memakai plaintext tersebut | Lolos logika pencarian normal |
| Enkripsi AES gagal (misal invalid Key) | Response diset [PROTECTED] |
Filter menelan error | Nilai riil dihapus dari payload | UI menampilkan [PROTECTED] |
| Field berjenis Integer dikonfigurasi sensitif | Response menjadi String [PROTECTED] |
N/A | Type JSON terubah | Kemungkinan error di Frontend parsers |
| State | Authoritative Current | Projection/Consumer |
|---|---|---|
| Dynamic fields target | SensitiveFieldConfigRepository (T_SENSITIVE_FIELD_CONFIG) |
Seluruh controller yang memanggil MaskService |
| Access Key User | T_USER.key |
DataAccessKeyService dan token evaluator |
| Temporary Valid Key | Memory: validationCache (Map) |
Cepatnya pengecekan sebelum DB hit |
| Cryptographic Secret Key | KeyService (Alias: aes-key-01) |
EncryptionService |
| Gap Current | Dampak |
|---|---|
Validasi static field sangat tight ke ExistingCurrTrans |
Tidak bisa mem-mask field khusus dari entity Alert/Case yang tidak di-share dengan Transaksi (kecuali diselipkan di addtData). |
| Data Access Key Cache per user tidak terhapus saat session diterminasi (hanya clear saat cron) | TTL 5 menit menyebabkan zombie validity window bagi token yang direvoke. |
Penggunaan literal string "[PROTECTED]" pada field non-String |
Mutasi skema JSON mendadak dan berisiko merusak type-safety client frontend. |
decryptIfEncrypted menangkap root Exception tanpa jejak audit |
Sangat sulit mendeteksi apakah filter input dari luar memang disengaja salah format atau ada insiden salah enkripsi. |
Masking iteratif menggunakan ObjectMapper.convertValue dari Object ke Map pada pagination besar |
Potensi overhead serialization/deserialization ganda tinggi pada /search yang melist ratusan object. |
| Surface | Endpoint Family | Capability |
|---|---|---|
| Sensitive Fields Management | /sensitive-fields |
Menambahkan (/add), menghapus (/delete/{fieldName}), melihat field aktif (/), dan melihat field ExistingCurrTrans yang available diregistrasi (/available). |
| Consumer (Transaction) | /existing-transaction/* |
Memakai header X-Data-Access-Key untuk unmask filter search dan unmask response. |
| Consumer (Alert Acquirer) | /alert-acquirer/* |
Memakai header mask di endpoint search dan findById. |
| Consumer (Alert Issuer) | /alert-issuer/* |
Memakai header mask di endpoint search dan findById. |
| Consumer (Case) | /case/* |
Memakai header mask di endpoint search. |
| Flow / Area | Primary Source |
|---|---|
| Orchestration & Nested AddtData | SecurityConfiguration/Security/Masking/TransactionMaskService.java |
| Token Generator & Rotation Cron | SecurityConfiguration/Security/Key/DataAccessKeyService.java |
| Field Restriction Logic | SecurityConfiguration/Security/Masking/SensitiveFieldConfigService.java |
| Fallback & Encryption Logic | SecurityConfiguration/Security/Encryption/EncryptionService.java |
| Sensitive Field Controller | SecurityConfiguration/Security/Masking/SensitiveFieldConfigController.java |
| Controller Injection & Unmask Invoke | AlertManagement/Case/CaseServiceImpl.java, GeneralComponent/Logic/ExistingCurrTrans/ExistingCurrTransController.java, dll. |
Saat memparsing endpoint pagination seperti /case/search, applyMaskingToGroupedPagination akan membongkar property bernama alerts di dalam masing-masing baris (apabila tipenya adalah List) dan mem-mask setiap item list tersebut secara terpisah mengandalkan iterasi map manual. Behavior ini spesifik disisipkan di MaskService saat ini agar nested object array tidak terlewat proses screening field aktif.