001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.transaction; 020 021import org.jpos.core.Configurable; 022import org.jpos.core.Configuration; 023import org.jpos.core.ConfigurationException; 024import org.jpos.iso.ISOException; 025import org.jpos.iso.ISOMsg; 026import org.jpos.iso.ISOUtil; 027import org.jpos.tlv.TLVList; 028import org.jpos.util.FSDMsg; 029import org.jpos.util.ProtectedLogListener; 030 031import java.io.Serializable; 032import java.util.Arrays; 033 034/** 035 *Sample Usage: 036 *<pre> 037 * <participant class="org.jpos.transaction.ProtectDebugInfo"> 038 * 039 * <property name="protect-entry" value="REQUEST" /> 040 * <property name="protect-entry" value="RESPONSE" /> 041 * <property name="protect-entry" value="PAN, EXP, REQUEST_ICC_DATA" /> 042 * <property name="wipe-entry" value="EXPDATE" /> 043 * 044 * <!-- if the protected ctx entry is an ISOMsg --> 045 * <property name="protect-ISOMsg" value="2" /> 046 * <property name="protect-ISOMsg" value="35, 45" /> 047 * <property name="wipe-ISOMsg" value="52, 55" /> 048 * 049 * <!-- if the protected ctx entry is a TLVList --> 050 * <property name="wipe-TLVList" value="0x56" /> 051 * <property name="wipe-TLVList" value="0x57" /> 052 * <property name="wipe-TLVList" value="0x5a, 0x5f20" /> 053 * 054 * <!-- if the protected ctx entry is a FSDMsg --> 055 * <property name="protect-FSDMsg" value="account-number" /> 056 * <property name="protect-FSDMsg" value="track2-data" /> 057 * <property name="wipe-FSDMsg" value="secret-key" /> 058 * 059 * </participant> 060 *</pre> 061 * 062 * Configuration properties accept comma/space-separated values, but can also be given in multiple occurrences. 063 * All occurrences of the same property will be merged into a single list. 064 * 065 * @author Alejandro Revilla 066 * @author David Bergert 067 * @author Barzilai Spinak 068 **/ 069 070public class ProtectDebugInfo implements AbortParticipant, Configurable { 071 /** Creates the participant; configuration is supplied via {@link #setConfiguration(Configuration)}. */ 072 public ProtectDebugInfo() {} 073 private static final String COMMA_SPACE_SEPARATOR = "[,\\s]+"; 074 private String[] protectedEntries; 075 private String[] wipedEntries; 076 private String[] protectFSD; 077 private String[] protectISO; 078 private String[] wipeISO; 079 private String[] wipeFSD; 080 private String[] wipeTLV; 081 082 public int prepare (long id, Serializable o) { 083 return PREPARED | READONLY; 084 } 085 public int prepareForAbort (long id, Serializable o) { 086 return PREPARED | READONLY; 087 } 088 public void commit (long id, Serializable o) { 089 protect ((Context) o); 090 } 091 public void abort (long id, Serializable o) { 092 protect ((Context) o); 093 } 094 095 private void protect (Context ctx) { 096 /* wipe by removing entries from the context */ 097 for (String s: wipedEntries) 098 ctx.remove(s); 099 100 /* Protect entry items */ 101 for (String s: protectedEntries) { 102 Object o = ctx.get (s); 103 104 if (o instanceof ISOMsg) { 105 ISOMsg m = ctx.get (s); 106 if (m != null) { 107 m = (ISOMsg) m.clone(); 108 ctx.put (s, m); // place a clone in the context 109 for (String p: protectISO) 110 protectField(m,p); 111 for (String p: wipeISO) 112 wipeField(m,p); 113 } 114 } 115 116 if (o instanceof FSDMsg){ 117 FSDMsg m = ctx.get (s); 118 if (m != null) { 119 for (String p: protectFSD) 120 protectField(m,p); 121 for (String p: wipeFSD) 122 wipeField(m,p); 123 } 124 } 125 126 if (o instanceof String){ 127 String p = ctx.get(s); 128 if (p != null){ 129 ctx.put(s, protect (p)); 130 } 131 } 132 133 if (o instanceof TLVList) { 134 TLVList tlv = ctx.get(s); 135 if (tlv != null) { 136 for (String t: wipeTLV) 137 wipeTag(tlv, t); 138 } 139 } 140 } 141 } 142 143 private void protectField (ISOMsg m, String f) { 144 if (m != null) { 145 m.set (f, protect (m.getString (f))); 146 } 147 } 148 149 private void wipeField (ISOMsg m, String f) { 150 if (m != null) { 151 try { 152 Object v = m.getValue(f); 153 if (v != null) { 154 if (v instanceof String) 155 m.set(f, ProtectedLogListener.WIPED); 156 else 157 m.set(f, ProtectedLogListener.BINARY_WIPED); 158 } 159 } catch (ISOException ignored) { 160 //ignore, valid routes for some messages in the context may not be valid for others 161 //e.g. in transaction switches with protocol conversion 162 } 163 } 164 } 165 166 static void wipeTag(TLVList tlv, String tag) { 167 if (tlv == null) 168 return; 169 try { 170 int tagName = Integer.decode(tag); 171 if (tlv.hasTag(tagName)) { 172 tlv.deleteByTag(tagName); 173 tlv.append(tagName, ProtectedLogListener.BINARY_WIPED); 174 } 175 } 176 catch (Throwable ignored) { } 177 } 178 179 private void protectField (FSDMsg m, String f) { 180 if (f != null) { 181 String s = m.get (f); 182 if (s != null) 183 m.set (f, ISOUtil.protect (s)); 184 } 185 } 186 187 private void wipeField (FSDMsg m, String f) { 188 if (m != null && m.get(f) != null) { 189 m.set (f, "*"); 190 } 191 } 192 193 private String protect (String s) { 194 return s != null ? ISOUtil.protect (s) : s; 195 } 196 197 public void setConfiguration (Configuration cfg) throws ConfigurationException { 198 this.protectedEntries = getValues(cfg, "protect-entry"); 199 this.wipedEntries = getValues(cfg, "wipe-entry"); 200 this.protectFSD = getValues(cfg, "protect-FSDMsg"); 201 this.protectISO = getValues(cfg, "protect-ISOMsg"); 202 this.wipeISO = getValues(cfg, "wipe-ISOMsg"); 203 this.wipeFSD = getValues(cfg, "wipe-FSDMsg"); 204 this.wipeTLV = getValues(cfg, "wipe-TLVList"); 205 } 206 private String[] getValues (Configuration cfg, String name) { 207 return Arrays.stream(cfg.getAll(name)) 208 .flatMap(v -> Arrays.stream(v.split(COMMA_SPACE_SEPARATOR))) 209 .map(String::trim) 210 .filter(v -> !v.isEmpty()) 211 .toArray(String[]::new); 212 } 213}