1: <?php
2: namespace Worldline\Acquiring\Sdk\Logging;
3:
4: use UnexpectedValueException;
5:
6: /**
7: * Class BodyObfuscator
8: *
9: * @package Worldline\Acquiring\Sdk\Logging
10: */
11: class BodyObfuscator
12: {
13: const MIME_APPLICATION_JSON = 'application/json';
14: const MIME_APPLICATION_PROBLEM_JSON = 'application/problem+json';
15:
16: /** @var ValueObfuscator */
17: protected $valueObfuscator;
18:
19: /** @var array<string, callable> */
20: private $customRules = array();
21:
22: public function __construct()
23: {
24: $this->valueObfuscator = new ValueObfuscator();
25: }
26:
27: /**
28: * @param string $contentType
29: * @param string $body
30: * @return string
31: */
32: public function obfuscateBody($contentType, $body)
33: {
34: if (!$this->isJsonContentType($contentType)) {
35: return $body;
36: }
37: $decodedJsonBody = json_decode($body);
38: if (json_last_error() !== JSON_ERROR_NONE) {
39: return $body;
40: }
41: return json_encode($this->obfuscateDecodedJsonPart($decodedJsonBody), JSON_PRETTY_PRINT);
42: }
43:
44: private function isJsonContentType($contentType)
45: {
46: return $contentType === static::MIME_APPLICATION_JSON
47: || $contentType === static::MIME_APPLICATION_PROBLEM_JSON
48: || substr($contentType, 0, strlen(static::MIME_APPLICATION_JSON)) === static::MIME_APPLICATION_JSON
49: || substr($contentType, 0, strlen(static::MIME_APPLICATION_PROBLEM_JSON)) === static::MIME_APPLICATION_PROBLEM_JSON;
50: }
51:
52: /**
53: * @param mixed $value
54: * @return mixed
55: */
56: protected function obfuscateDecodedJsonPart($value)
57: {
58: if (is_object($value)) {
59: foreach ($value as $propertyName => $propertyValue) {
60: if (is_scalar($propertyValue)) {
61: $value->$propertyName = $this->obfuscateScalarValue($propertyName, $propertyValue);
62: } else {
63: $value->$propertyName = $this->obfuscateDecodedJsonPart($propertyValue);
64: }
65: }
66: }
67: if (is_array($value)) {
68: foreach ($value as $elementKey => &$elementValue) {
69: if (is_scalar($elementValue)) {
70: $elementValue = $this->obfuscateScalarValue($elementKey, $elementValue);
71: } else {
72: $elementValue = $this->obfuscateDecodedJsonPart($elementValue);
73: }
74: }
75:
76: }
77: return $value;
78: }
79:
80: /**
81: * @param string $key
82: * @param scalar $value
83: * @return string
84: */
85: protected function obfuscateScalarValue($key, $value)
86: {
87: if (!is_scalar($value)) {
88: throw new UnexpectedValueException('scalar value expected');
89: }
90: $lowerKey = mb_strtolower(strval($key), 'UTF-8');
91: if (isset($this->customRules[$lowerKey])) {
92: return call_user_func($this->customRules[$lowerKey], $value, $this->valueObfuscator);
93: }
94: switch ($lowerKey) {
95: case 'address':
96: case 'cardholderaddress':
97: case 'cardholderpostalcode':
98: case 'cardsecuritycode':
99: case 'city':
100: case 'name':
101: case 'postalcode':
102: case 'statecode':
103: return $this->valueObfuscator->obfuscateAll($value);
104: case 'bin':
105: case 'paymentaccountreference':
106: return $this->valueObfuscator->obfuscateAllKeepStart($value, 6);
107: case 'authenticationvalue':
108: case 'cryptogram':
109: return $this->valueObfuscator->obfuscateAllKeepStart($value, 4);
110: case 'cardnumber':
111: case 'expirydate':
112: return $this->valueObfuscator->obfuscateAllKeepEnd($value, 4);
113: default:
114: return $value;
115: }
116: }
117:
118: /**
119: * @param string $propertyName
120: * @param callable $customRule
121: */
122: public function setCustomRule($propertyName, callable $customRule)
123: {
124: $lowerName = mb_strtolower(strval($propertyName), 'UTF-8');
125: $this->customRules[$lowerName] = $customRule;
126: }
127: }
128: